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_MOVE_STYLE TRUE *1
32 #define USE_NEW_MOVE_DELAY TRUE *1
33 #define USE_NEW_PUSH_DELAY TRUE *1
34 #define USE_NEW_BLOCK_STYLE TRUE *1
41 /* for MovePlayer() */
42 #define MF_NO_ACTION 0
46 /* for ScrollPlayer() */
48 #define SCROLL_GO_ON 1
51 #define EX_PHASE_START 0
52 #define EX_TYPE_NONE 0
53 #define EX_TYPE_NORMAL (1 << 0)
54 #define EX_TYPE_CENTER (1 << 1)
55 #define EX_TYPE_BORDER (1 << 2)
56 #define EX_TYPE_CROSS (1 << 3)
57 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
59 /* special positions in the game control window (relative to control window) */
62 #define XX_EMERALDS 29
63 #define YY_EMERALDS 54
64 #define XX_DYNAMITE 29
65 #define YY_DYNAMITE 89
74 /* special positions in the game control window (relative to main window) */
75 #define DX_LEVEL (DX + XX_LEVEL)
76 #define DY_LEVEL (DY + YY_LEVEL)
77 #define DX_EMERALDS (DX + XX_EMERALDS)
78 #define DY_EMERALDS (DY + YY_EMERALDS)
79 #define DX_DYNAMITE (DX + XX_DYNAMITE)
80 #define DY_DYNAMITE (DY + YY_DYNAMITE)
81 #define DX_KEYS (DX + XX_KEYS)
82 #define DY_KEYS (DY + YY_KEYS)
83 #define DX_SCORE (DX + XX_SCORE)
84 #define DY_SCORE (DY + YY_SCORE)
85 #define DX_TIME1 (DX + XX_TIME1)
86 #define DX_TIME2 (DX + XX_TIME2)
87 #define DY_TIME (DY + YY_TIME)
89 /* values for initial player move delay (initial delay counter value) */
90 #define INITIAL_MOVE_DELAY_OFF -1
91 #define INITIAL_MOVE_DELAY_ON 0
93 /* values for player movement speed (which is in fact a delay value) */
94 #define MOVE_DELAY_NORMAL_SPEED 8
95 #define MOVE_DELAY_HIGH_SPEED 4
97 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
98 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
99 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
100 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
102 /* values for other actions */
103 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
105 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
106 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
108 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
110 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
111 RND(element_info[e].push_delay_random))
112 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
113 RND(element_info[e].drop_delay_random))
114 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
115 RND(element_info[e].move_delay_random))
116 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
117 (element_info[e].move_delay_random))
119 #define GET_TARGET_ELEMENT(e, ch) \
120 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
121 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
123 #define GET_VALID_PLAYER_ELEMENT(e) \
124 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
126 #define CAN_GROW_INTO(e) \
127 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
129 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
133 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
135 (CAN_MOVE_INTO_ACID(e) && \
136 Feld[x][y] == EL_ACID) || \
139 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
141 (CAN_MOVE_INTO_ACID(e) && \
142 Feld[x][y] == EL_ACID) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 (CAN_MOVE_INTO_ACID(e) && \
149 Feld[x][y] == EL_ACID) || \
150 (DONT_COLLIDE_WITH(e) && \
152 !PLAYER_ENEMY_PROTECTED(x, y))))
155 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
158 (DONT_COLLIDE_WITH(e) && \
160 !PLAYER_ENEMY_PROTECTED(x, y))))
163 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
164 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
167 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
168 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
170 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
171 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
175 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
178 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
183 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
186 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
189 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
192 #define PIG_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
195 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
197 IS_FOOD_PENGUIN(Feld[x][y])))
198 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
201 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
204 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
211 (CAN_MOVE_INTO_ACID(e) && \
212 Feld[x][y] == EL_ACID) || \
213 Feld[x][y] == EL_DIAMOND))
215 #define DARK_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 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
221 #define PACMAN_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_AMOEBOID(Feld[x][y])))
227 #define PIG_CAN_ENTER_FIELD(e, x, y) \
228 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
229 (CAN_MOVE_INTO_ACID(e) && \
230 Feld[x][y] == EL_ACID) || \
231 IS_FOOD_PIG(Feld[x][y])))
233 #define PENGUIN_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_PENGUIN(Feld[x][y]) || \
238 Feld[x][y] == EL_EXIT_OPEN))
240 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
241 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
242 (CAN_MOVE_INTO_ACID(e) && \
243 Feld[x][y] == EL_ACID)))
245 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
246 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
247 (CAN_MOVE_INTO_ACID(e) && \
248 Feld[x][y] == EL_ACID) || \
251 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
252 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
253 (CAN_MOVE_INTO_ACID(e) && \
254 Feld[x][y] == EL_ACID)))
258 #define GROUP_NR(e) ((e) - EL_GROUP_START)
259 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
260 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
261 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
263 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
264 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
267 #define CE_ENTER_FIELD_COND(e, x, y) \
268 (!IS_PLAYER(x, y) && \
269 (Feld[x][y] == EL_ACID || \
270 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
272 #define CE_ENTER_FIELD_COND(e, x, y) \
273 (!IS_PLAYER(x, y) && \
274 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
277 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
278 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
280 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
281 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
283 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
284 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
285 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
286 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
288 /* game button identifiers */
289 #define GAME_CTRL_ID_STOP 0
290 #define GAME_CTRL_ID_PAUSE 1
291 #define GAME_CTRL_ID_PLAY 2
292 #define SOUND_CTRL_ID_MUSIC 3
293 #define SOUND_CTRL_ID_LOOPS 4
294 #define SOUND_CTRL_ID_SIMPLE 5
296 #define NUM_GAME_BUTTONS 6
299 /* forward declaration for internal use */
301 static void AdvanceFrameAndPlayerCounters(int);
303 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
304 static boolean MovePlayer(struct PlayerInfo *, int, int);
305 static void ScrollPlayer(struct PlayerInfo *, int);
306 static void ScrollScreen(struct PlayerInfo *, int);
308 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
310 static void InitBeltMovement(void);
311 static void CloseAllOpenTimegates(void);
312 static void CheckGravityMovement(struct PlayerInfo *);
313 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
314 static void KillHeroUnlessEnemyProtected(int, int);
315 static void KillHeroUnlessExplosionProtected(int, int);
317 static void TestIfPlayerTouchesCustomElement(int, int);
318 static void TestIfElementTouchesCustomElement(int, int);
319 static void TestIfElementHitsCustomElement(int, int, int);
321 static void TestIfElementSmashesCustomElement(int, int, int);
324 static void ChangeElement(int, int, int);
326 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
327 #define CheckTriggeredElementChange(x, y, e, ev) \
328 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
330 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
331 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
332 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
333 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
334 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
335 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
338 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
339 #define CheckElementChange(x, y, e, te, ev) \
340 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
341 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
342 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
343 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
344 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
345 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
346 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
348 static void PlayLevelSound(int, int, int);
349 static void PlayLevelSoundNearest(int, int, int);
350 static void PlayLevelSoundAction(int, int, int);
351 static void PlayLevelSoundElementAction(int, int, int, int);
352 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
353 static void PlayLevelSoundActionIfLoop(int, int, int);
354 static void StopLevelSoundActionIfLoop(int, int, int);
355 static void PlayLevelMusic();
357 static void MapGameButtons();
358 static void HandleGameButtons(struct GadgetInfo *);
360 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
363 /* ------------------------------------------------------------------------- */
364 /* definition of elements that automatically change to other elements after */
365 /* a specified time, eventually calling a function when changing */
366 /* ------------------------------------------------------------------------- */
368 /* forward declaration for changer functions */
369 static void InitBuggyBase(int x, int y);
370 static void WarnBuggyBase(int x, int y);
372 static void InitTrap(int x, int y);
373 static void ActivateTrap(int x, int y);
374 static void ChangeActiveTrap(int x, int y);
376 static void InitRobotWheel(int x, int y);
377 static void RunRobotWheel(int x, int y);
378 static void StopRobotWheel(int x, int y);
380 static void InitTimegateWheel(int x, int y);
381 static void RunTimegateWheel(int x, int y);
383 struct ChangingElementInfo
388 void (*pre_change_function)(int x, int y);
389 void (*change_function)(int x, int y);
390 void (*post_change_function)(int x, int y);
393 static struct ChangingElementInfo change_delay_list[] =
395 #if USE_NEW_BLOCK_STYLE
398 EL_PLAYER_IS_LEAVING,
400 -1, /* delay for blocking field left by player set at runtime */
456 EL_SWITCHGATE_OPENING,
464 EL_SWITCHGATE_CLOSING,
465 EL_SWITCHGATE_CLOSED,
497 EL_ACID_SPLASH_RIGHT,
506 EL_SP_BUGGY_BASE_ACTIVATING,
513 EL_SP_BUGGY_BASE_ACTIVATING,
514 EL_SP_BUGGY_BASE_ACTIVE,
521 EL_SP_BUGGY_BASE_ACTIVE,
545 EL_ROBOT_WHEEL_ACTIVE,
553 EL_TIMEGATE_SWITCH_ACTIVE,
574 int push_delay_fixed, push_delay_random;
579 { EL_BALLOON, 0, 0 },
581 { EL_SOKOBAN_OBJECT, 2, 0 },
582 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
583 { EL_SATELLITE, 2, 0 },
584 { EL_SP_DISK_YELLOW, 2, 0 },
586 { EL_UNDEFINED, 0, 0 },
594 move_stepsize_list[] =
596 { EL_AMOEBA_DROP, 2 },
597 { EL_AMOEBA_DROPPING, 2 },
598 { EL_QUICKSAND_FILLING, 1 },
599 { EL_QUICKSAND_EMPTYING, 1 },
600 { EL_MAGIC_WALL_FILLING, 2 },
601 { EL_BD_MAGIC_WALL_FILLING, 2 },
602 { EL_MAGIC_WALL_EMPTYING, 2 },
603 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
613 collect_count_list[] =
616 { EL_BD_DIAMOND, 1 },
617 { EL_EMERALD_YELLOW, 1 },
618 { EL_EMERALD_RED, 1 },
619 { EL_EMERALD_PURPLE, 1 },
621 { EL_SP_INFOTRON, 1 },
633 access_direction_list[] =
635 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
636 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
637 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
638 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
639 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
640 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
641 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
642 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
643 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
644 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
645 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
647 { EL_SP_PORT_LEFT, MV_RIGHT },
648 { EL_SP_PORT_RIGHT, MV_LEFT },
649 { EL_SP_PORT_UP, MV_DOWN },
650 { EL_SP_PORT_DOWN, MV_UP },
651 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
652 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
653 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
654 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
655 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
656 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
657 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
658 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
659 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
660 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
661 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
662 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
663 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
664 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
665 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
667 { EL_UNDEFINED, MV_NO_MOVING }
670 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
672 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
673 CH_EVENT_BIT(CE_DELAY))
674 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
675 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
676 IS_JUST_CHANGING(x, y))
678 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
681 void GetPlayerConfig()
683 if (!audio.sound_available)
684 setup.sound_simple = FALSE;
686 if (!audio.loops_available)
687 setup.sound_loops = FALSE;
689 if (!audio.music_available)
690 setup.sound_music = FALSE;
692 if (!video.fullscreen_available)
693 setup.fullscreen = FALSE;
695 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
697 SetAudioMode(setup.sound);
701 static int getBeltNrFromBeltElement(int element)
703 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
704 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
705 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
708 static int getBeltNrFromBeltActiveElement(int element)
710 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
711 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
712 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
715 static int getBeltNrFromBeltSwitchElement(int element)
717 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
718 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
719 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
722 static int getBeltDirNrFromBeltSwitchElement(int element)
724 static int belt_base_element[4] =
726 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
727 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
728 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
729 EL_CONVEYOR_BELT_4_SWITCH_LEFT
732 int belt_nr = getBeltNrFromBeltSwitchElement(element);
733 int belt_dir_nr = element - belt_base_element[belt_nr];
735 return (belt_dir_nr % 3);
738 static int getBeltDirFromBeltSwitchElement(int element)
740 static int belt_move_dir[3] =
747 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
749 return belt_move_dir[belt_dir_nr];
752 static void InitPlayerField(int x, int y, int element, boolean init_game)
754 if (element == EL_SP_MURPHY)
758 if (stored_player[0].present)
760 Feld[x][y] = EL_SP_MURPHY_CLONE;
766 stored_player[0].use_murphy_graphic = TRUE;
769 Feld[x][y] = EL_PLAYER_1;
775 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
776 int jx = player->jx, jy = player->jy;
778 player->present = TRUE;
780 player->block_last_field = (element == EL_SP_MURPHY ?
781 level.sp_block_last_field :
782 level.block_last_field);
784 #if USE_NEW_BLOCK_STYLE
786 player->block_delay = (player->block_last_field ?
787 (element == EL_SP_MURPHY ?
788 level.sp_block_delay :
789 level.block_delay) : 0);
791 player->block_delay = (element == EL_SP_MURPHY ?
792 (player->block_last_field ? 7 : 1) :
793 (player->block_last_field ? 7 : 1));
797 if (!options.network || player->connected)
799 player->active = TRUE;
801 /* remove potentially duplicate players */
802 if (StorePlayer[jx][jy] == Feld[x][y])
803 StorePlayer[jx][jy] = 0;
805 StorePlayer[x][y] = Feld[x][y];
809 printf("Player %d activated.\n", player->element_nr);
810 printf("[Local player is %d and currently %s.]\n",
811 local_player->element_nr,
812 local_player->active ? "active" : "not active");
816 Feld[x][y] = EL_EMPTY;
818 player->jx = player->last_jx = x;
819 player->jy = player->last_jy = y;
823 static void InitField(int x, int y, boolean init_game)
825 int element = Feld[x][y];
834 InitPlayerField(x, y, element, init_game);
837 case EL_SOKOBAN_FIELD_PLAYER:
838 element = Feld[x][y] = EL_PLAYER_1;
839 InitField(x, y, init_game);
841 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
842 InitField(x, y, init_game);
845 case EL_SOKOBAN_FIELD_EMPTY:
846 local_player->sokobanfields_still_needed++;
850 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
851 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
852 else if (x > 0 && Feld[x-1][y] == EL_ACID)
853 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
854 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
855 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
856 else if (y > 0 && Feld[x][y-1] == EL_ACID)
857 Feld[x][y] = EL_ACID_POOL_BOTTOM;
858 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
859 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
867 case EL_SPACESHIP_RIGHT:
868 case EL_SPACESHIP_UP:
869 case EL_SPACESHIP_LEFT:
870 case EL_SPACESHIP_DOWN:
872 case EL_BD_BUTTERFLY_RIGHT:
873 case EL_BD_BUTTERFLY_UP:
874 case EL_BD_BUTTERFLY_LEFT:
875 case EL_BD_BUTTERFLY_DOWN:
876 case EL_BD_BUTTERFLY:
877 case EL_BD_FIREFLY_RIGHT:
878 case EL_BD_FIREFLY_UP:
879 case EL_BD_FIREFLY_LEFT:
880 case EL_BD_FIREFLY_DOWN:
882 case EL_PACMAN_RIGHT:
906 if (y == lev_fieldy - 1)
908 Feld[x][y] = EL_AMOEBA_GROWING;
909 Store[x][y] = EL_AMOEBA_WET;
913 case EL_DYNAMITE_ACTIVE:
914 case EL_SP_DISK_RED_ACTIVE:
915 case EL_DYNABOMB_PLAYER_1_ACTIVE:
916 case EL_DYNABOMB_PLAYER_2_ACTIVE:
917 case EL_DYNABOMB_PLAYER_3_ACTIVE:
918 case EL_DYNABOMB_PLAYER_4_ACTIVE:
923 local_player->lights_still_needed++;
927 local_player->friends_still_needed++;
932 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
937 Feld[x][y] = EL_EMPTY;
942 case EL_EM_KEY_1_FILE:
943 Feld[x][y] = EL_EM_KEY_1;
945 case EL_EM_KEY_2_FILE:
946 Feld[x][y] = EL_EM_KEY_2;
948 case EL_EM_KEY_3_FILE:
949 Feld[x][y] = EL_EM_KEY_3;
951 case EL_EM_KEY_4_FILE:
952 Feld[x][y] = EL_EM_KEY_4;
956 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
957 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
958 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
959 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
960 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
961 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
962 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
963 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
964 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
965 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
966 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
967 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
970 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
971 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
972 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
974 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
976 game.belt_dir[belt_nr] = belt_dir;
977 game.belt_dir_nr[belt_nr] = belt_dir_nr;
979 else /* more than one switch -- set it like the first switch */
981 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
986 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
988 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
991 case EL_LIGHT_SWITCH_ACTIVE:
993 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
997 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
999 else if (IS_GROUP_ELEMENT(element))
1001 struct ElementGroupInfo *group = element_info[element].group;
1002 int last_anim_random_frame = gfx.anim_random_frame;
1005 if (group->choice_mode == ANIM_RANDOM)
1006 gfx.anim_random_frame = RND(group->num_elements_resolved);
1008 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1009 group->choice_mode, 0,
1012 if (group->choice_mode == ANIM_RANDOM)
1013 gfx.anim_random_frame = last_anim_random_frame;
1015 group->choice_pos++;
1017 Feld[x][y] = group->element_resolved[element_pos];
1019 InitField(x, y, init_game);
1025 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1027 InitField(x, y, init_game);
1029 /* not needed to call InitMovDir() -- already done by InitField()! */
1030 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1031 CAN_MOVE(Feld[x][y]))
1035 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1037 int old_element = Feld[x][y];
1039 InitField(x, y, init_game);
1041 /* not needed to call InitMovDir() -- already done by InitField()! */
1042 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1043 CAN_MOVE(old_element) &&
1044 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1047 /* this case is in fact a combination of not less than three bugs:
1048 first, it calls InitMovDir() for elements that can move, although this is
1049 already done by InitField(); then, it checks the element that was at this
1050 field _before_ the call to InitField() (which can change it); lastly, it
1051 was not called for "mole with direction" elements, which were treated as
1052 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1056 inline void DrawGameValue_Emeralds(int value)
1058 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1061 inline void DrawGameValue_Dynamite(int value)
1063 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1066 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1070 for (i = 0; i < MAX_KEYS; i++)
1072 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1073 el2edimg(EL_KEY_1 + i));
1076 inline void DrawGameValue_Score(int value)
1078 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1081 inline void DrawGameValue_Time(int value)
1084 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1086 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1089 inline void DrawGameValue_Level(int value)
1092 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1095 /* misuse area for displaying emeralds to draw bigger level number */
1096 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1097 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1099 /* now copy it to the area for displaying level number */
1100 BlitBitmap(drawto, drawto,
1101 DX_EMERALDS, DY_EMERALDS + 1,
1102 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1103 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1104 DX_LEVEL - 1, DY_LEVEL + 1);
1106 /* restore the area for displaying emeralds */
1107 DrawGameValue_Emeralds(local_player->gems_still_needed);
1109 /* yes, this is all really ugly :-) */
1113 void DrawGameDoorValues()
1117 DrawGameValue_Level(level_nr);
1119 for (i = 0; i < MAX_PLAYERS; i++)
1120 DrawGameValue_Keys(&stored_player[i]);
1122 DrawGameValue_Emeralds(local_player->gems_still_needed);
1123 DrawGameValue_Dynamite(local_player->inventory_size);
1124 DrawGameValue_Score(local_player->score);
1125 DrawGameValue_Time(TimeLeft);
1128 static void resolve_group_element(int group_element, int recursion_depth)
1130 static int group_nr;
1131 static struct ElementGroupInfo *group;
1132 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1135 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1137 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1138 group_element - EL_GROUP_START + 1);
1140 /* replace element which caused too deep recursion by question mark */
1141 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1146 if (recursion_depth == 0) /* initialization */
1148 group = element_info[group_element].group;
1149 group_nr = group_element - EL_GROUP_START;
1151 group->num_elements_resolved = 0;
1152 group->choice_pos = 0;
1155 for (i = 0; i < actual_group->num_elements; i++)
1157 int element = actual_group->element[i];
1159 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1162 if (IS_GROUP_ELEMENT(element))
1163 resolve_group_element(element, recursion_depth + 1);
1166 group->element_resolved[group->num_elements_resolved++] = element;
1167 element_info[element].in_group[group_nr] = TRUE;
1172 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1174 printf("::: group %d: %d resolved elements\n",
1175 group_element - EL_GROUP_START, group->num_elements_resolved);
1176 for (i = 0; i < group->num_elements_resolved; i++)
1177 printf("::: - %d ['%s']\n", group->element_resolved[i],
1178 element_info[group->element_resolved[i]].token_name);
1185 =============================================================================
1187 -----------------------------------------------------------------------------
1188 initialize game engine due to level / tape version number
1189 =============================================================================
1192 static void InitGameEngine()
1196 /* set game engine from tape file when re-playing, else from level file */
1197 game.engine_version = (tape.playing ? tape.engine_version :
1198 level.game_version);
1200 /* dynamically adjust element properties according to game engine version */
1201 InitElementPropertiesEngine(game.engine_version);
1204 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1205 printf(" tape version == %06d [%s] [file: %06d]\n",
1206 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1208 printf(" => game.engine_version == %06d\n", game.engine_version);
1211 /* ---------- recursively resolve group elements ------------------------- */
1213 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1214 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1215 element_info[i].in_group[j] = FALSE;
1217 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1218 resolve_group_element(EL_GROUP_START + i, 0);
1220 /* ---------- initialize player's initial move delay --------------------- */
1222 #if USE_NEW_MOVE_DELAY
1223 /* dynamically adjust player properties according to level information */
1224 game.initial_move_delay_value =
1225 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1227 /* dynamically adjust player properties according to game engine version */
1228 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1229 game.initial_move_delay_value : 0);
1231 /* dynamically adjust player properties according to game engine version */
1232 game.initial_move_delay =
1233 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1234 INITIAL_MOVE_DELAY_OFF);
1236 /* dynamically adjust player properties according to level information */
1237 game.initial_move_delay_value =
1238 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1241 /* ---------- initialize player's initial push delay --------------------- */
1243 /* dynamically adjust player properties according to game engine version */
1244 game.initial_push_delay_value =
1245 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1247 /* ---------- initialize changing elements ------------------------------- */
1249 /* initialize changing elements information */
1250 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1252 struct ElementInfo *ei = &element_info[i];
1254 /* this pointer might have been changed in the level editor */
1255 ei->change = &ei->change_page[0];
1257 if (!IS_CUSTOM_ELEMENT(i))
1259 ei->change->target_element = EL_EMPTY_SPACE;
1260 ei->change->delay_fixed = 0;
1261 ei->change->delay_random = 0;
1262 ei->change->delay_frames = 1;
1265 ei->change_events = CE_BITMASK_DEFAULT;
1266 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1268 ei->event_page_nr[j] = 0;
1269 ei->event_page[j] = &ei->change_page[0];
1273 /* add changing elements from pre-defined list */
1274 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1276 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1277 struct ElementInfo *ei = &element_info[ch_delay->element];
1279 ei->change->target_element = ch_delay->target_element;
1280 ei->change->delay_fixed = ch_delay->change_delay;
1282 ei->change->pre_change_function = ch_delay->pre_change_function;
1283 ei->change->change_function = ch_delay->change_function;
1284 ei->change->post_change_function = ch_delay->post_change_function;
1286 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1289 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1294 /* add change events from custom element configuration */
1295 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1297 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1299 for (j = 0; j < ei->num_change_pages; j++)
1301 if (!ei->change_page[j].can_change)
1304 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1306 /* only add event page for the first page found with this event */
1307 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1308 !(ei->change_events & CH_EVENT_BIT(k)))
1310 ei->change_events |= CH_EVENT_BIT(k);
1311 ei->event_page_nr[k] = j;
1312 ei->event_page[k] = &ei->change_page[j];
1320 /* add change events from custom element configuration */
1321 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1323 int element = EL_CUSTOM_START + i;
1325 /* only add custom elements that change after fixed/random frame delay */
1326 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1327 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1331 /* ---------- initialize run-time trigger player and element ------------- */
1333 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1335 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1337 for (j = 0; j < ei->num_change_pages; j++)
1339 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1340 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1344 /* ---------- initialize trigger events ---------------------------------- */
1346 /* initialize trigger events information */
1347 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1348 trigger_events[i] = EP_BITMASK_DEFAULT;
1351 /* add trigger events from element change event properties */
1352 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1354 struct ElementInfo *ei = &element_info[i];
1356 for (j = 0; j < ei->num_change_pages; j++)
1358 if (!ei->change_page[j].can_change)
1361 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1363 int trigger_element = ei->change_page[j].trigger_element;
1365 if (IS_GROUP_ELEMENT(trigger_element))
1367 struct ElementGroupInfo *group = element_info[trigger_element].group;
1369 for (k = 0; k < group->num_elements_resolved; k++)
1370 trigger_events[group->element_resolved[k]]
1371 |= ei->change_page[j].events;
1374 trigger_events[trigger_element] |= ei->change_page[j].events;
1379 /* add trigger events from element change event properties */
1380 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1381 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1382 trigger_events[element_info[i].change->trigger_element] |=
1383 element_info[i].change->events;
1386 /* ---------- initialize push delay -------------------------------------- */
1388 /* initialize push delay values to default */
1389 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1391 if (!IS_CUSTOM_ELEMENT(i))
1393 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1394 element_info[i].push_delay_random = game.default_push_delay_random;
1398 /* set push delay value for certain elements from pre-defined list */
1399 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1401 int e = push_delay_list[i].element;
1403 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1404 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1407 /* set push delay value for Supaplex elements for newer engine versions */
1408 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1410 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1412 if (IS_SP_ELEMENT(i))
1414 #if USE_NEW_MOVE_STYLE
1415 /* set SP push delay to just enough to push under a falling zonk */
1416 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1418 element_info[i].push_delay_fixed = delay;
1419 element_info[i].push_delay_random = 0;
1421 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1422 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1428 /* ---------- initialize move stepsize ----------------------------------- */
1430 /* initialize move stepsize values to default */
1431 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1432 if (!IS_CUSTOM_ELEMENT(i))
1433 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1435 /* set move stepsize value for certain elements from pre-defined list */
1436 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1438 int e = move_stepsize_list[i].element;
1440 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1444 /* ---------- initialize move dig/leave ---------------------------------- */
1446 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1448 element_info[i].can_leave_element = FALSE;
1449 element_info[i].can_leave_element_last = FALSE;
1453 /* ---------- initialize gem count --------------------------------------- */
1455 /* initialize gem count values for each element */
1456 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1457 if (!IS_CUSTOM_ELEMENT(i))
1458 element_info[i].collect_count = 0;
1460 /* add gem count values for all elements from pre-defined list */
1461 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1462 element_info[collect_count_list[i].element].collect_count =
1463 collect_count_list[i].count;
1465 /* ---------- initialize access direction -------------------------------- */
1467 /* initialize access direction values to default (access from every side) */
1468 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1469 if (!IS_CUSTOM_ELEMENT(i))
1470 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1472 /* set access direction value for certain elements from pre-defined list */
1473 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1474 element_info[access_direction_list[i].element].access_direction =
1475 access_direction_list[i].direction;
1480 =============================================================================
1482 -----------------------------------------------------------------------------
1483 initialize and start new game
1484 =============================================================================
1489 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1490 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1491 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1498 #if USE_NEW_AMOEBA_CODE
1499 printf("Using new amoeba code.\n");
1501 printf("Using old amoeba code.\n");
1506 /* don't play tapes over network */
1507 network_playing = (options.network && !tape.playing);
1509 for (i = 0; i < MAX_PLAYERS; i++)
1511 struct PlayerInfo *player = &stored_player[i];
1513 player->index_nr = i;
1514 player->index_bit = (1 << i);
1515 player->element_nr = EL_PLAYER_1 + i;
1517 player->present = FALSE;
1518 player->active = FALSE;
1521 player->effective_action = 0;
1522 player->programmed_action = 0;
1525 player->gems_still_needed = level.gems_needed;
1526 player->sokobanfields_still_needed = 0;
1527 player->lights_still_needed = 0;
1528 player->friends_still_needed = 0;
1530 for (j = 0; j < MAX_KEYS; j++)
1531 player->key[j] = FALSE;
1533 player->dynabomb_count = 0;
1534 player->dynabomb_size = 1;
1535 player->dynabombs_left = 0;
1536 player->dynabomb_xl = FALSE;
1538 player->MovDir = MV_NO_MOVING;
1541 player->GfxDir = MV_NO_MOVING;
1542 player->GfxAction = ACTION_DEFAULT;
1544 player->StepFrame = 0;
1546 player->use_murphy_graphic = FALSE;
1548 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1549 player->block_delay = -1; /* initialized in InitPlayerField() */
1551 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1553 player->actual_frame_counter = 0;
1555 player->step_counter = 0;
1557 player->last_move_dir = MV_NO_MOVING;
1559 player->is_waiting = FALSE;
1560 player->is_moving = FALSE;
1561 player->is_auto_moving = FALSE;
1562 player->is_digging = FALSE;
1563 player->is_snapping = FALSE;
1564 player->is_collecting = FALSE;
1565 player->is_pushing = FALSE;
1566 player->is_switching = FALSE;
1567 player->is_dropping = FALSE;
1569 player->is_bored = FALSE;
1570 player->is_sleeping = FALSE;
1572 player->frame_counter_bored = -1;
1573 player->frame_counter_sleeping = -1;
1575 player->anim_delay_counter = 0;
1576 player->post_delay_counter = 0;
1578 player->action_waiting = ACTION_DEFAULT;
1579 player->last_action_waiting = ACTION_DEFAULT;
1580 player->special_action_bored = ACTION_DEFAULT;
1581 player->special_action_sleeping = ACTION_DEFAULT;
1583 player->num_special_action_bored = 0;
1584 player->num_special_action_sleeping = 0;
1586 /* determine number of special actions for bored and sleeping animation */
1587 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1589 boolean found = FALSE;
1591 for (k = 0; k < NUM_DIRECTIONS; k++)
1592 if (el_act_dir2img(player->element_nr, j, k) !=
1593 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1597 player->num_special_action_bored++;
1601 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1603 boolean found = FALSE;
1605 for (k = 0; k < NUM_DIRECTIONS; k++)
1606 if (el_act_dir2img(player->element_nr, j, k) !=
1607 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1611 player->num_special_action_sleeping++;
1616 player->switch_x = -1;
1617 player->switch_y = -1;
1619 player->show_envelope = 0;
1621 player->move_delay = game.initial_move_delay;
1622 player->move_delay_value = game.initial_move_delay_value;
1624 player->move_delay_reset_counter = 0;
1626 #if USE_NEW_PUSH_DELAY
1627 player->push_delay = -1; /* initialized when pushing starts */
1628 player->push_delay_value = game.initial_push_delay_value;
1630 player->push_delay = 0;
1631 player->push_delay_value = game.initial_push_delay_value;
1634 player->drop_delay = 0;
1636 player->last_jx = player->last_jy = 0;
1637 player->jx = player->jy = 0;
1639 player->shield_normal_time_left = 0;
1640 player->shield_deadly_time_left = 0;
1642 player->inventory_infinite_element = EL_UNDEFINED;
1643 player->inventory_size = 0;
1645 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1646 SnapField(player, 0, 0);
1648 player->LevelSolved = FALSE;
1649 player->GameOver = FALSE;
1652 network_player_action_received = FALSE;
1654 #if defined(NETWORK_AVALIABLE)
1655 /* initial null action */
1656 if (network_playing)
1657 SendToServer_MovePlayer(MV_NO_MOVING);
1665 TimeLeft = level.time;
1668 ScreenMovDir = MV_NO_MOVING;
1672 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1674 AllPlayersGone = FALSE;
1676 game.yamyam_content_nr = 0;
1677 game.magic_wall_active = FALSE;
1678 game.magic_wall_time_left = 0;
1679 game.light_time_left = 0;
1680 game.timegate_time_left = 0;
1681 game.switchgate_pos = 0;
1682 game.balloon_dir = MV_NO_MOVING;
1683 game.gravity = level.initial_gravity;
1684 game.explosions_delayed = TRUE;
1686 game.envelope_active = FALSE;
1688 for (i = 0; i < NUM_BELTS; i++)
1690 game.belt_dir[i] = MV_NO_MOVING;
1691 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1694 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1695 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1697 for (x = 0; x < lev_fieldx; x++)
1699 for (y = 0; y < lev_fieldy; y++)
1701 Feld[x][y] = level.field[x][y];
1702 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1703 ChangeDelay[x][y] = 0;
1704 ChangePage[x][y] = -1;
1705 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1707 WasJustMoving[x][y] = 0;
1708 WasJustFalling[x][y] = 0;
1709 CheckCollision[x][y] = 0;
1711 Pushed[x][y] = FALSE;
1713 Changed[x][y] = CE_BITMASK_DEFAULT;
1714 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1716 ExplodePhase[x][y] = 0;
1717 ExplodeDelay[x][y] = 0;
1718 ExplodeField[x][y] = EX_TYPE_NONE;
1720 RunnerVisit[x][y] = 0;
1721 PlayerVisit[x][y] = 0;
1724 GfxRandom[x][y] = INIT_GFX_RANDOM();
1725 GfxElement[x][y] = EL_UNDEFINED;
1726 GfxAction[x][y] = ACTION_DEFAULT;
1727 GfxDir[x][y] = MV_NO_MOVING;
1731 for (y = 0; y < lev_fieldy; y++)
1733 for (x = 0; x < lev_fieldx; x++)
1735 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1737 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1739 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1742 InitField(x, y, TRUE);
1748 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1749 emulate_sb ? EMU_SOKOBAN :
1750 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1752 /* initialize explosion and ignition delay */
1753 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1755 if (!IS_CUSTOM_ELEMENT(i))
1758 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1759 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1760 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1761 int last_phase = (num_phase + 1) * delay;
1762 int half_phase = (num_phase / 2) * delay;
1764 element_info[i].explosion_delay = last_phase - 1;
1765 element_info[i].ignition_delay = half_phase;
1768 if (i == EL_BLACK_ORB)
1769 element_info[i].ignition_delay = 0;
1771 if (i == EL_BLACK_ORB)
1772 element_info[i].ignition_delay = 1;
1777 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1778 element_info[i].explosion_delay = 1;
1780 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1781 element_info[i].ignition_delay = 1;
1785 /* correct non-moving belts to start moving left */
1786 for (i = 0; i < NUM_BELTS; i++)
1787 if (game.belt_dir[i] == MV_NO_MOVING)
1788 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1790 /* check if any connected player was not found in playfield */
1791 for (i = 0; i < MAX_PLAYERS; i++)
1793 struct PlayerInfo *player = &stored_player[i];
1795 if (player->connected && !player->present)
1797 for (j = 0; j < MAX_PLAYERS; j++)
1799 struct PlayerInfo *some_player = &stored_player[j];
1800 int jx = some_player->jx, jy = some_player->jy;
1802 /* assign first free player found that is present in the playfield */
1803 if (some_player->present && !some_player->connected)
1805 player->present = TRUE;
1806 player->active = TRUE;
1808 some_player->present = FALSE;
1809 some_player->active = FALSE;
1812 player->element_nr = some_player->element_nr;
1815 StorePlayer[jx][jy] = player->element_nr;
1816 player->jx = player->last_jx = jx;
1817 player->jy = player->last_jy = jy;
1827 /* when playing a tape, eliminate all players which do not participate */
1829 for (i = 0; i < MAX_PLAYERS; i++)
1831 if (stored_player[i].active && !tape.player_participates[i])
1833 struct PlayerInfo *player = &stored_player[i];
1834 int jx = player->jx, jy = player->jy;
1836 player->active = FALSE;
1837 StorePlayer[jx][jy] = 0;
1838 Feld[jx][jy] = EL_EMPTY;
1842 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1844 /* when in single player mode, eliminate all but the first active player */
1846 for (i = 0; i < MAX_PLAYERS; i++)
1848 if (stored_player[i].active)
1850 for (j = i + 1; j < MAX_PLAYERS; j++)
1852 if (stored_player[j].active)
1854 struct PlayerInfo *player = &stored_player[j];
1855 int jx = player->jx, jy = player->jy;
1857 player->active = FALSE;
1858 player->present = FALSE;
1860 StorePlayer[jx][jy] = 0;
1861 Feld[jx][jy] = EL_EMPTY;
1868 /* when recording the game, store which players take part in the game */
1871 for (i = 0; i < MAX_PLAYERS; i++)
1872 if (stored_player[i].active)
1873 tape.player_participates[i] = TRUE;
1878 for (i = 0; i < MAX_PLAYERS; i++)
1880 struct PlayerInfo *player = &stored_player[i];
1882 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1887 if (local_player == player)
1888 printf("Player %d is local player.\n", i+1);
1892 if (BorderElement == EL_EMPTY)
1895 SBX_Right = lev_fieldx - SCR_FIELDX;
1897 SBY_Lower = lev_fieldy - SCR_FIELDY;
1902 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1904 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1907 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1908 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1910 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1911 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1913 /* if local player not found, look for custom element that might create
1914 the player (make some assumptions about the right custom element) */
1915 if (!local_player->present)
1917 int start_x = 0, start_y = 0;
1918 int found_rating = 0;
1919 int found_element = EL_UNDEFINED;
1921 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1923 int element = Feld[x][y];
1928 if (!IS_CUSTOM_ELEMENT(element))
1931 if (CAN_CHANGE(element))
1933 for (i = 0; i < element_info[element].num_change_pages; i++)
1935 content = element_info[element].change_page[i].target_element;
1936 is_player = ELEM_IS_PLAYER(content);
1938 if (is_player && (found_rating < 3 || element < found_element))
1944 found_element = element;
1949 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1951 content = element_info[element].content[xx][yy];
1952 is_player = ELEM_IS_PLAYER(content);
1954 if (is_player && (found_rating < 2 || element < found_element))
1956 start_x = x + xx - 1;
1957 start_y = y + yy - 1;
1960 found_element = element;
1963 if (!CAN_CHANGE(element))
1966 for (i = 0; i < element_info[element].num_change_pages; i++)
1968 content= element_info[element].change_page[i].target_content[xx][yy];
1969 is_player = ELEM_IS_PLAYER(content);
1971 if (is_player && (found_rating < 1 || element < found_element))
1973 start_x = x + xx - 1;
1974 start_y = y + yy - 1;
1977 found_element = element;
1983 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1984 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1987 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1988 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1994 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1995 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1996 local_player->jx - MIDPOSX);
1998 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1999 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2000 local_player->jy - MIDPOSY);
2002 scroll_x = SBX_Left;
2003 scroll_y = SBY_Upper;
2004 if (local_player->jx >= SBX_Left + MIDPOSX)
2005 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2006 local_player->jx - MIDPOSX :
2008 if (local_player->jy >= SBY_Upper + MIDPOSY)
2009 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2010 local_player->jy - MIDPOSY :
2015 CloseDoor(DOOR_CLOSE_1);
2020 /* after drawing the level, correct some elements */
2021 if (game.timegate_time_left == 0)
2022 CloseAllOpenTimegates();
2024 if (setup.soft_scrolling)
2025 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2027 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2030 /* copy default game door content to main double buffer */
2031 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2032 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2034 DrawGameDoorValues();
2038 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2039 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2040 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2044 /* copy actual game door content to door double buffer for OpenDoor() */
2045 BlitBitmap(drawto, bitmap_db_door,
2046 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2048 OpenDoor(DOOR_OPEN_ALL);
2050 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2052 if (setup.sound_music)
2055 KeyboardAutoRepeatOffUnlessAutoplay();
2059 for (i = 0; i < MAX_PLAYERS; i++)
2060 printf("Player %d %sactive.\n",
2061 i + 1, (stored_player[i].active ? "" : "not "));
2065 printf("::: starting game [%d]\n", FrameCounter);
2069 void InitMovDir(int x, int y)
2071 int i, element = Feld[x][y];
2072 static int xy[4][2] =
2079 static int direction[3][4] =
2081 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2082 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2083 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2092 Feld[x][y] = EL_BUG;
2093 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2096 case EL_SPACESHIP_RIGHT:
2097 case EL_SPACESHIP_UP:
2098 case EL_SPACESHIP_LEFT:
2099 case EL_SPACESHIP_DOWN:
2100 Feld[x][y] = EL_SPACESHIP;
2101 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2104 case EL_BD_BUTTERFLY_RIGHT:
2105 case EL_BD_BUTTERFLY_UP:
2106 case EL_BD_BUTTERFLY_LEFT:
2107 case EL_BD_BUTTERFLY_DOWN:
2108 Feld[x][y] = EL_BD_BUTTERFLY;
2109 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2112 case EL_BD_FIREFLY_RIGHT:
2113 case EL_BD_FIREFLY_UP:
2114 case EL_BD_FIREFLY_LEFT:
2115 case EL_BD_FIREFLY_DOWN:
2116 Feld[x][y] = EL_BD_FIREFLY;
2117 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2120 case EL_PACMAN_RIGHT:
2122 case EL_PACMAN_LEFT:
2123 case EL_PACMAN_DOWN:
2124 Feld[x][y] = EL_PACMAN;
2125 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2128 case EL_SP_SNIKSNAK:
2129 MovDir[x][y] = MV_UP;
2132 case EL_SP_ELECTRON:
2133 MovDir[x][y] = MV_LEFT;
2140 Feld[x][y] = EL_MOLE;
2141 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2145 if (IS_CUSTOM_ELEMENT(element))
2147 struct ElementInfo *ei = &element_info[element];
2148 int move_direction_initial = ei->move_direction_initial;
2149 int move_pattern = ei->move_pattern;
2151 if (move_direction_initial == MV_START_PREVIOUS)
2153 if (MovDir[x][y] != MV_NO_MOVING)
2156 move_direction_initial = MV_START_AUTOMATIC;
2159 if (move_direction_initial == MV_START_RANDOM)
2160 MovDir[x][y] = 1 << RND(4);
2161 else if (move_direction_initial & MV_ANY_DIRECTION)
2162 MovDir[x][y] = move_direction_initial;
2163 else if (move_pattern == MV_ALL_DIRECTIONS ||
2164 move_pattern == MV_TURNING_LEFT ||
2165 move_pattern == MV_TURNING_RIGHT ||
2166 move_pattern == MV_TURNING_LEFT_RIGHT ||
2167 move_pattern == MV_TURNING_RIGHT_LEFT ||
2168 move_pattern == MV_TURNING_RANDOM)
2169 MovDir[x][y] = 1 << RND(4);
2170 else if (move_pattern == MV_HORIZONTAL)
2171 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2172 else if (move_pattern == MV_VERTICAL)
2173 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2174 else if (move_pattern & MV_ANY_DIRECTION)
2175 MovDir[x][y] = element_info[element].move_pattern;
2176 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2177 move_pattern == MV_ALONG_RIGHT_SIDE)
2180 /* use random direction as default start direction */
2181 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2182 MovDir[x][y] = 1 << RND(4);
2185 for (i = 0; i < NUM_DIRECTIONS; i++)
2187 int x1 = x + xy[i][0];
2188 int y1 = y + xy[i][1];
2190 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2192 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2193 MovDir[x][y] = direction[0][i];
2195 MovDir[x][y] = direction[1][i];
2204 MovDir[x][y] = 1 << RND(4);
2206 if (element != EL_BUG &&
2207 element != EL_SPACESHIP &&
2208 element != EL_BD_BUTTERFLY &&
2209 element != EL_BD_FIREFLY)
2212 for (i = 0; i < NUM_DIRECTIONS; i++)
2214 int x1 = x + xy[i][0];
2215 int y1 = y + xy[i][1];
2217 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2219 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2221 MovDir[x][y] = direction[0][i];
2224 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2225 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2227 MovDir[x][y] = direction[1][i];
2236 GfxDir[x][y] = MovDir[x][y];
2239 void InitAmoebaNr(int x, int y)
2242 int group_nr = AmoebeNachbarNr(x, y);
2246 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2248 if (AmoebaCnt[i] == 0)
2256 AmoebaNr[x][y] = group_nr;
2257 AmoebaCnt[group_nr]++;
2258 AmoebaCnt2[group_nr]++;
2264 boolean raise_level = FALSE;
2266 if (local_player->MovPos)
2270 if (tape.auto_play) /* tape might already be stopped here */
2271 tape.auto_play_level_solved = TRUE;
2273 if (tape.playing && tape.auto_play)
2274 tape.auto_play_level_solved = TRUE;
2277 local_player->LevelSolved = FALSE;
2279 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2283 if (!tape.playing && setup.sound_loops)
2284 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2285 SND_CTRL_PLAY_LOOP);
2287 while (TimeLeft > 0)
2289 if (!tape.playing && !setup.sound_loops)
2290 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2291 if (TimeLeft > 0 && !(TimeLeft % 10))
2292 RaiseScore(level.score[SC_TIME_BONUS]);
2293 if (TimeLeft > 100 && !(TimeLeft % 10))
2298 DrawGameValue_Time(TimeLeft);
2306 if (!tape.playing && setup.sound_loops)
2307 StopSound(SND_GAME_LEVELTIME_BONUS);
2309 else if (level.time == 0) /* level without time limit */
2311 if (!tape.playing && setup.sound_loops)
2312 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2313 SND_CTRL_PLAY_LOOP);
2315 while (TimePlayed < 999)
2317 if (!tape.playing && !setup.sound_loops)
2318 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2319 if (TimePlayed < 999 && !(TimePlayed % 10))
2320 RaiseScore(level.score[SC_TIME_BONUS]);
2321 if (TimePlayed < 900 && !(TimePlayed % 10))
2326 DrawGameValue_Time(TimePlayed);
2334 if (!tape.playing && setup.sound_loops)
2335 StopSound(SND_GAME_LEVELTIME_BONUS);
2338 /* close exit door after last player */
2339 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2340 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2342 int element = Feld[ExitX][ExitY];
2344 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2345 EL_SP_EXIT_CLOSING);
2347 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2350 /* Hero disappears */
2351 DrawLevelField(ExitX, ExitY);
2357 CloseDoor(DOOR_CLOSE_1);
2362 SaveTape(tape.level_nr); /* Ask to save tape */
2365 if (level_nr == leveldir_current->handicap_level)
2367 leveldir_current->handicap_level++;
2368 SaveLevelSetup_SeriesInfo();
2371 if (level_editor_test_game)
2372 local_player->score = -1; /* no highscore when playing from editor */
2373 else if (level_nr < leveldir_current->last_level)
2374 raise_level = TRUE; /* advance to next level */
2376 if ((hi_pos = NewHiScore()) >= 0)
2378 game_status = GAME_MODE_SCORES;
2379 DrawHallOfFame(hi_pos);
2388 game_status = GAME_MODE_MAIN;
2405 LoadScore(level_nr);
2407 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2408 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2411 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2413 if (local_player->score > highscore[k].Score)
2415 /* player has made it to the hall of fame */
2417 if (k < MAX_SCORE_ENTRIES - 1)
2419 int m = MAX_SCORE_ENTRIES - 1;
2422 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2423 if (!strcmp(setup.player_name, highscore[l].Name))
2425 if (m == k) /* player's new highscore overwrites his old one */
2429 for (l = m; l > k; l--)
2431 strcpy(highscore[l].Name, highscore[l - 1].Name);
2432 highscore[l].Score = highscore[l - 1].Score;
2439 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2440 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2441 highscore[k].Score = local_player->score;
2447 else if (!strncmp(setup.player_name, highscore[k].Name,
2448 MAX_PLAYER_NAME_LEN))
2449 break; /* player already there with a higher score */
2455 SaveScore(level_nr);
2460 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2462 if (player->GfxAction != action || player->GfxDir != dir)
2465 printf("Player frame reset! (%d => %d, %d => %d)\n",
2466 player->GfxAction, action, player->GfxDir, dir);
2469 player->GfxAction = action;
2470 player->GfxDir = dir;
2472 player->StepFrame = 0;
2476 static void ResetRandomAnimationValue(int x, int y)
2478 GfxRandom[x][y] = INIT_GFX_RANDOM();
2481 static void ResetGfxAnimation(int x, int y)
2484 GfxAction[x][y] = ACTION_DEFAULT;
2485 GfxDir[x][y] = MovDir[x][y];
2488 void InitMovingField(int x, int y, int direction)
2490 int element = Feld[x][y];
2491 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2492 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2496 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2497 ResetGfxAnimation(x, y);
2499 MovDir[newx][newy] = MovDir[x][y] = direction;
2500 GfxDir[x][y] = direction;
2502 if (Feld[newx][newy] == EL_EMPTY)
2503 Feld[newx][newy] = EL_BLOCKED;
2505 if (direction == MV_DOWN && CAN_FALL(element))
2506 GfxAction[x][y] = ACTION_FALLING;
2508 GfxAction[x][y] = ACTION_MOVING;
2510 GfxFrame[newx][newy] = GfxFrame[x][y];
2511 GfxRandom[newx][newy] = GfxRandom[x][y];
2512 GfxAction[newx][newy] = GfxAction[x][y];
2513 GfxDir[newx][newy] = GfxDir[x][y];
2516 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2518 int direction = MovDir[x][y];
2519 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2520 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2526 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2528 int oldx = x, oldy = y;
2529 int direction = MovDir[x][y];
2531 if (direction == MV_LEFT)
2533 else if (direction == MV_RIGHT)
2535 else if (direction == MV_UP)
2537 else if (direction == MV_DOWN)
2540 *comes_from_x = oldx;
2541 *comes_from_y = oldy;
2544 int MovingOrBlocked2Element(int x, int y)
2546 int element = Feld[x][y];
2548 if (element == EL_BLOCKED)
2552 Blocked2Moving(x, y, &oldx, &oldy);
2553 return Feld[oldx][oldy];
2559 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2561 /* like MovingOrBlocked2Element(), but if element is moving
2562 and (x,y) is the field the moving element is just leaving,
2563 return EL_BLOCKED instead of the element value */
2564 int element = Feld[x][y];
2566 if (IS_MOVING(x, y))
2568 if (element == EL_BLOCKED)
2572 Blocked2Moving(x, y, &oldx, &oldy);
2573 return Feld[oldx][oldy];
2582 static void RemoveField(int x, int y)
2584 Feld[x][y] = EL_EMPTY;
2591 ChangeDelay[x][y] = 0;
2592 ChangePage[x][y] = -1;
2593 Pushed[x][y] = FALSE;
2596 ExplodeField[x][y] = EX_TYPE_NONE;
2599 GfxElement[x][y] = EL_UNDEFINED;
2600 GfxAction[x][y] = ACTION_DEFAULT;
2601 GfxDir[x][y] = MV_NO_MOVING;
2604 void RemoveMovingField(int x, int y)
2606 int oldx = x, oldy = y, newx = x, newy = y;
2607 int element = Feld[x][y];
2608 int next_element = EL_UNDEFINED;
2610 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2613 if (IS_MOVING(x, y))
2615 Moving2Blocked(x, y, &newx, &newy);
2617 if (Feld[newx][newy] != EL_BLOCKED)
2620 if (Feld[newx][newy] != EL_BLOCKED)
2622 /* element is moving, but target field is not free (blocked), but
2623 already occupied by something different (example: acid pool);
2624 in this case, only remove the moving field, but not the target */
2626 RemoveField(oldx, oldy);
2628 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2630 DrawLevelField(oldx, oldy);
2636 else if (element == EL_BLOCKED)
2638 Blocked2Moving(x, y, &oldx, &oldy);
2639 if (!IS_MOVING(oldx, oldy))
2643 if (element == EL_BLOCKED &&
2644 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2645 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2646 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2647 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2648 next_element = get_next_element(Feld[oldx][oldy]);
2650 RemoveField(oldx, oldy);
2651 RemoveField(newx, newy);
2653 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2655 if (next_element != EL_UNDEFINED)
2656 Feld[oldx][oldy] = next_element;
2658 DrawLevelField(oldx, oldy);
2659 DrawLevelField(newx, newy);
2662 void DrawDynamite(int x, int y)
2664 int sx = SCREENX(x), sy = SCREENY(y);
2665 int graphic = el2img(Feld[x][y]);
2668 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2671 if (IS_WALKABLE_INSIDE(Back[x][y]))
2675 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2676 else if (Store[x][y])
2677 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2679 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2682 if (Back[x][y] || Store[x][y])
2683 DrawGraphicThruMask(sx, sy, graphic, frame);
2685 DrawGraphic(sx, sy, graphic, frame);
2687 if (game.emulation == EMU_SUPAPLEX)
2688 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2689 else if (Store[x][y])
2690 DrawGraphicThruMask(sx, sy, graphic, frame);
2692 DrawGraphic(sx, sy, graphic, frame);
2696 void CheckDynamite(int x, int y)
2698 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2702 if (MovDelay[x][y] != 0)
2705 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2712 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2714 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2715 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2716 StopSound(SND_DYNAMITE_ACTIVE);
2718 StopSound(SND_DYNABOMB_ACTIVE);
2724 void DrawRelocatePlayer(struct PlayerInfo *player)
2726 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2727 boolean no_delay = (tape.warp_forward);
2728 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2729 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2730 int jx = player->jx;
2731 int jy = player->jy;
2733 if (level.instant_relocation)
2736 int offset = (setup.scroll_delay ? 3 : 0);
2738 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2740 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2741 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2742 local_player->jx - MIDPOSX);
2744 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2745 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2746 local_player->jy - MIDPOSY);
2750 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2751 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2752 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2754 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2755 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2756 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2758 /* don't scroll over playfield boundaries */
2759 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2760 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2762 /* don't scroll over playfield boundaries */
2763 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2764 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2767 scroll_x += (local_player->jx - old_jx);
2768 scroll_y += (local_player->jy - old_jy);
2770 /* don't scroll over playfield boundaries */
2771 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2772 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2774 /* don't scroll over playfield boundaries */
2775 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2776 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2779 RedrawPlayfield(TRUE, 0,0,0,0);
2785 int offset = (setup.scroll_delay ? 3 : 0);
2787 int scroll_xx = -999, scroll_yy = -999;
2789 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2791 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2794 int fx = FX, fy = FY;
2796 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2797 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2798 local_player->jx - MIDPOSX);
2800 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2801 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2802 local_player->jy - MIDPOSY);
2804 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2805 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2808 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2811 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2818 fx += dx * TILEX / 2;
2819 fy += dy * TILEY / 2;
2821 ScrollLevel(dx, dy);
2824 /* scroll in two steps of half tile size to make things smoother */
2825 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2827 Delay(wait_delay_value);
2829 /* scroll second step to align at full tile size */
2831 Delay(wait_delay_value);
2834 int scroll_xx = -999, scroll_yy = -999;
2836 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2838 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2841 int fx = FX, fy = FY;
2843 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2844 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2845 local_player->jx - MIDPOSX);
2847 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2848 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2849 local_player->jy - MIDPOSY);
2851 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2852 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2855 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2858 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2865 fx += dx * TILEX / 2;
2866 fy += dy * TILEY / 2;
2868 ScrollLevel(dx, dy);
2871 /* scroll in two steps of half tile size to make things smoother */
2872 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2874 Delay(wait_delay_value);
2876 /* scroll second step to align at full tile size */
2878 Delay(wait_delay_value);
2884 Delay(wait_delay_value);
2888 void RelocatePlayer(int jx, int jy, int el_player_raw)
2891 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2893 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2895 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2896 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2897 boolean no_delay = (tape.warp_forward);
2898 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2899 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2900 int old_jx = player->jx;
2901 int old_jy = player->jy;
2902 int old_element = Feld[old_jx][old_jy];
2903 int element = Feld[jx][jy];
2904 boolean player_relocated = (old_jx != jx || old_jy != jy);
2906 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2907 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2909 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2910 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2911 int leave_side_horiz = move_dir_horiz;
2912 int leave_side_vert = move_dir_vert;
2914 static int trigger_sides[4][2] =
2916 /* enter side leave side */
2917 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2918 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2919 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2920 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2922 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2923 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2924 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2925 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2927 int enter_side = enter_side_horiz | enter_side_vert;
2928 int leave_side = leave_side_horiz | leave_side_vert;
2930 if (player->GameOver) /* do not reanimate dead player */
2933 if (!player_relocated) /* no need to relocate the player */
2936 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2938 RemoveField(jx, jy); /* temporarily remove newly placed player */
2939 DrawLevelField(jx, jy);
2942 if (player->present)
2944 while (player->MovPos)
2946 ScrollPlayer(player, SCROLL_GO_ON);
2947 ScrollScreen(NULL, SCROLL_GO_ON);
2949 #if USE_NEW_MOVE_DELAY
2950 AdvanceFrameAndPlayerCounters(player->index_nr);
2958 Delay(wait_delay_value);
2961 DrawPlayer(player); /* needed here only to cleanup last field */
2962 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2964 player->is_moving = FALSE;
2968 if (IS_CUSTOM_ELEMENT(old_element))
2969 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
2971 player->index_bit, leave_side);
2973 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
2975 player->index_bit, leave_side);
2978 Feld[jx][jy] = el_player;
2979 InitPlayerField(jx, jy, el_player, TRUE);
2981 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
2983 Feld[jx][jy] = element;
2984 InitField(jx, jy, FALSE);
2988 if (player == local_player) /* only visually relocate local player */
2989 DrawRelocatePlayer(player);
2993 TestIfHeroTouchesBadThing(jx, jy);
2994 TestIfPlayerTouchesCustomElement(jx, jy);
2998 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3003 /* needed to allow change of walkable custom element by entering player */
3004 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3005 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3007 /* needed to allow change of walkable custom element by entering player */
3008 Changed[jx][jy] = 0; /* allow another change */
3013 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3014 enter_side == MV_LEFT ? "left" :
3015 enter_side == MV_RIGHT ? "right" :
3016 enter_side == MV_UP ? "top" :
3017 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3021 if (IS_CUSTOM_ELEMENT(element))
3022 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3023 player->index_bit, enter_side);
3025 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3026 CE_OTHER_GETS_ENTERED,
3027 player->index_bit, enter_side);
3031 void Explode(int ex, int ey, int phase, int mode)
3038 /* !!! eliminate this variable !!! */
3039 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3044 int last_phase = num_phase * delay;
3045 int half_phase = (num_phase / 2) * delay;
3046 int first_phase_after_start = EX_PHASE_START + 1;
3050 if (game.explosions_delayed)
3052 ExplodeField[ex][ey] = mode;
3056 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3058 int center_element = Feld[ex][ey];
3061 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3065 /* --- This is only really needed (and now handled) in "Impact()". --- */
3066 /* do not explode moving elements that left the explode field in time */
3067 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3068 center_element == EL_EMPTY &&
3069 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3073 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3074 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3076 /* remove things displayed in background while burning dynamite */
3077 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3080 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3082 /* put moving element to center field (and let it explode there) */
3083 center_element = MovingOrBlocked2Element(ex, ey);
3084 RemoveMovingField(ex, ey);
3085 Feld[ex][ey] = center_element;
3091 last_phase = element_info[center_element].explosion_delay + 1;
3093 last_phase = element_info[center_element].explosion_delay;
3097 printf("::: %d -> %d\n", center_element, last_phase);
3101 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3103 int xx = x - ex + 1;
3104 int yy = y - ey + 1;
3109 if (!IN_LEV_FIELD(x, y) ||
3110 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3111 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3114 if (!IN_LEV_FIELD(x, y) ||
3115 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3119 if (!IN_LEV_FIELD(x, y) ||
3120 ((mode != EX_TYPE_NORMAL ||
3121 center_element == EL_AMOEBA_TO_DIAMOND) &&
3122 (x != ex || y != ey)))
3126 element = Feld[x][y];
3128 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3130 element = MovingOrBlocked2Element(x, y);
3132 if (!IS_EXPLOSION_PROOF(element))
3133 RemoveMovingField(x, y);
3139 if (IS_EXPLOSION_PROOF(element))
3142 /* indestructible elements can only explode in center (but not flames) */
3144 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3145 mode == EX_TYPE_BORDER)) ||
3146 element == EL_FLAMES)
3149 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3150 element == EL_FLAMES)
3156 if ((IS_INDESTRUCTIBLE(element) &&
3157 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3158 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3159 element == EL_FLAMES)
3164 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3165 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3166 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3168 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3171 if (IS_ACTIVE_BOMB(element))
3173 /* re-activate things under the bomb like gate or penguin */
3175 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3178 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3183 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3184 element_info[Feld[x][y]].token_name,
3185 Store[x][y], Store2[x][y]);
3192 /* save walkable background elements while explosion on same tile */
3194 if (IS_INDESTRUCTIBLE(element))
3195 Back[x][y] = element;
3199 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3200 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3201 Back[x][y] = element;
3203 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3204 (x != ex || y != ey))
3205 Back[x][y] = element;
3208 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3209 Back[x][y] = element;
3213 /* ignite explodable elements reached by other explosion */
3214 if (element == EL_EXPLOSION)
3215 element = Store2[x][y];
3218 if (AmoebaNr[x][y] &&
3219 (element == EL_AMOEBA_FULL ||
3220 element == EL_BD_AMOEBA ||
3221 element == EL_AMOEBA_GROWING))
3223 AmoebaCnt[AmoebaNr[x][y]]--;
3224 AmoebaCnt2[AmoebaNr[x][y]]--;
3230 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3232 switch(StorePlayer[ex][ey])
3235 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3238 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3241 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3245 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3250 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3251 Store[x][y] = EL_EMPTY;
3253 if (game.emulation == EMU_SUPAPLEX)
3254 Store[x][y] = EL_EMPTY;
3257 else if (center_element == EL_MOLE)
3258 Store[x][y] = EL_EMERALD_RED;
3259 else if (center_element == EL_PENGUIN)
3260 Store[x][y] = EL_EMERALD_PURPLE;
3261 else if (center_element == EL_BUG)
3262 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3263 else if (center_element == EL_BD_BUTTERFLY)
3264 Store[x][y] = EL_BD_DIAMOND;
3265 else if (center_element == EL_SP_ELECTRON)
3266 Store[x][y] = EL_SP_INFOTRON;
3267 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3268 Store[x][y] = level.amoeba_content;
3269 else if (center_element == EL_YAMYAM)
3270 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3271 else if (IS_CUSTOM_ELEMENT(center_element) &&
3272 element_info[center_element].content[xx][yy] != EL_EMPTY)
3273 Store[x][y] = element_info[center_element].content[xx][yy];
3274 else if (element == EL_WALL_EMERALD)
3275 Store[x][y] = EL_EMERALD;
3276 else if (element == EL_WALL_DIAMOND)
3277 Store[x][y] = EL_DIAMOND;
3278 else if (element == EL_WALL_BD_DIAMOND)
3279 Store[x][y] = EL_BD_DIAMOND;
3280 else if (element == EL_WALL_EMERALD_YELLOW)
3281 Store[x][y] = EL_EMERALD_YELLOW;
3282 else if (element == EL_WALL_EMERALD_RED)
3283 Store[x][y] = EL_EMERALD_RED;
3284 else if (element == EL_WALL_EMERALD_PURPLE)
3285 Store[x][y] = EL_EMERALD_PURPLE;
3286 else if (element == EL_WALL_PEARL)
3287 Store[x][y] = EL_PEARL;
3288 else if (element == EL_WALL_CRYSTAL)
3289 Store[x][y] = EL_CRYSTAL;
3290 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3291 Store[x][y] = element_info[element].content[1][1];
3293 Store[x][y] = EL_EMPTY;
3295 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3296 center_element == EL_AMOEBA_TO_DIAMOND)
3297 Store2[x][y] = element;
3300 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3301 element_info[Store2[x][y]].token_name);
3305 if (AmoebaNr[x][y] &&
3306 (element == EL_AMOEBA_FULL ||
3307 element == EL_BD_AMOEBA ||
3308 element == EL_AMOEBA_GROWING))
3310 AmoebaCnt[AmoebaNr[x][y]]--;
3311 AmoebaCnt2[AmoebaNr[x][y]]--;
3317 MovDir[x][y] = MovPos[x][y] = 0;
3318 GfxDir[x][y] = MovDir[x][y];
3323 Feld[x][y] = EL_EXPLOSION;
3325 GfxElement[x][y] = center_element;
3327 GfxElement[x][y] = EL_UNDEFINED;
3330 ExplodePhase[x][y] = 1;
3332 ExplodeDelay[x][y] = last_phase;
3337 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3339 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3346 if (center_element == EL_YAMYAM)
3347 game.yamyam_content_nr =
3348 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3351 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3352 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3366 GfxFrame[x][y] = 0; /* restart explosion animation */
3370 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3374 last_phase = ExplodeDelay[x][y];
3377 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3381 /* activate this even in non-DEBUG version until cause for crash in
3382 getGraphicAnimationFrame() (see below) is found and eliminated */
3386 if (GfxElement[x][y] == EL_UNDEFINED)
3389 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3390 printf("Explode(): This should never happen!\n");
3393 GfxElement[x][y] = EL_EMPTY;
3399 border_element = Store2[x][y];
3401 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3402 border_element = StorePlayer[x][y];
3404 if (IS_PLAYER(x, y))
3405 border_element = StorePlayer[x][y];
3409 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3410 element_info[border_element].token_name, Store2[x][y]);
3414 printf("::: phase == %d\n", phase);
3417 if (phase == element_info[border_element].ignition_delay ||
3418 phase == last_phase)
3420 boolean border_explosion = FALSE;
3424 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3425 !PLAYER_EXPLOSION_PROTECTED(x, y))
3427 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3430 if (IS_PLAYER(x, y))
3433 KillHeroUnlessExplosionProtected(x, y);
3434 border_explosion = TRUE;
3437 if (phase == last_phase)
3438 printf("::: IS_PLAYER\n");
3441 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3444 printf("::: %d,%d: %d %s\n", x, y, border_element,
3445 element_info[border_element].token_name);
3448 Feld[x][y] = Store2[x][y];
3451 border_explosion = TRUE;
3454 if (phase == last_phase)
3455 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3458 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3460 AmoebeUmwandeln(x, y);
3462 border_explosion = TRUE;
3465 if (phase == last_phase)
3466 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3467 element_info[border_element].explosion_delay,
3468 element_info[border_element].ignition_delay,
3474 /* if an element just explodes due to another explosion (chain-reaction),
3475 do not immediately end the new explosion when it was the last frame of
3476 the explosion (as it would be done in the following "if"-statement!) */
3477 if (border_explosion && phase == last_phase)
3484 if (phase == first_phase_after_start)
3486 int element = Store2[x][y];
3488 if (element == EL_BLACK_ORB)
3490 Feld[x][y] = Store2[x][y];
3495 else if (phase == half_phase)
3497 int element = Store2[x][y];
3499 if (IS_PLAYER(x, y))
3500 KillHeroUnlessExplosionProtected(x, y);
3501 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3503 Feld[x][y] = Store2[x][y];
3507 else if (element == EL_AMOEBA_TO_DIAMOND)
3508 AmoebeUmwandeln(x, y);
3512 if (phase == last_phase)
3517 printf("::: done: phase == %d\n", phase);
3521 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3524 element = Feld[x][y] = Store[x][y];
3525 Store[x][y] = Store2[x][y] = 0;
3526 GfxElement[x][y] = EL_UNDEFINED;
3528 /* player can escape from explosions and might therefore be still alive */
3529 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3530 element <= EL_PLAYER_IS_EXPLODING_4)
3531 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3533 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3534 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3535 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3538 /* restore probably existing indestructible background element */
3539 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3540 element = Feld[x][y] = Back[x][y];
3543 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3544 GfxDir[x][y] = MV_NO_MOVING;
3545 ChangeDelay[x][y] = 0;
3546 ChangePage[x][y] = -1;
3549 InitField_WithBug2(x, y, FALSE);
3551 InitField(x, y, FALSE);
3553 /* !!! not needed !!! */
3555 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3556 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3559 if (CAN_MOVE(element))
3564 DrawLevelField(x, y);
3566 TestIfElementTouchesCustomElement(x, y);
3568 if (GFX_CRUMBLED(element))
3569 DrawLevelFieldCrumbledSandNeighbours(x, y);
3571 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3572 StorePlayer[x][y] = 0;
3574 if (ELEM_IS_PLAYER(element))
3575 RelocatePlayer(x, y, element);
3578 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3580 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3584 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3586 int stored = Store[x][y];
3587 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3588 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3592 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3594 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3598 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3602 printf("::: %d / %d [%d - %d]\n",
3603 GfxFrame[x][y], phase - delay, phase, delay);
3607 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3608 element_info[GfxElement[x][y]].token_name,
3613 DrawLevelFieldCrumbledSand(x, y);
3615 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3617 DrawLevelElement(x, y, Back[x][y]);
3618 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3620 else if (IS_WALKABLE_UNDER(Back[x][y]))
3622 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3623 DrawLevelElementThruMask(x, y, Back[x][y]);
3625 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3626 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3630 void DynaExplode(int ex, int ey)
3633 int dynabomb_element = Feld[ex][ey];
3634 int dynabomb_size = 1;
3635 boolean dynabomb_xl = FALSE;
3636 struct PlayerInfo *player;
3637 static int xy[4][2] =
3645 if (IS_ACTIVE_BOMB(dynabomb_element))
3647 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3648 dynabomb_size = player->dynabomb_size;
3649 dynabomb_xl = player->dynabomb_xl;
3650 player->dynabombs_left++;
3653 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3655 for (i = 0; i < NUM_DIRECTIONS; i++)
3657 for (j = 1; j <= dynabomb_size; j++)
3659 int x = ex + j * xy[i][0];
3660 int y = ey + j * xy[i][1];
3663 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3666 element = Feld[x][y];
3668 /* do not restart explosions of fields with active bombs */
3669 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3672 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3676 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3677 !IS_DIGGABLE(element) && !dynabomb_xl)
3680 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3681 !CAN_GROW_INTO(element) && !dynabomb_xl)
3685 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3686 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3687 element != EL_SAND && !dynabomb_xl)
3694 void Bang(int x, int y)
3697 int element = MovingOrBlocked2Element(x, y);
3699 int element = Feld[x][y];
3703 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3705 if (IS_PLAYER(x, y))
3708 struct PlayerInfo *player = PLAYERINFO(x, y);
3710 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3711 player->element_nr);
3716 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3718 if (game.emulation == EMU_SUPAPLEX)
3719 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3721 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3726 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3734 case EL_BD_BUTTERFLY:
3737 case EL_DARK_YAMYAM:
3741 RaiseScoreElement(element);
3742 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3744 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3745 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3746 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3747 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3748 case EL_DYNABOMB_INCREASE_NUMBER:
3749 case EL_DYNABOMB_INCREASE_SIZE:
3750 case EL_DYNABOMB_INCREASE_POWER:
3755 case EL_LAMP_ACTIVE:
3757 case EL_AMOEBA_TO_DIAMOND:
3759 if (IS_PLAYER(x, y))
3760 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3762 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3766 if (element_info[element].explosion_type == EXPLODES_CROSS)
3768 if (CAN_EXPLODE_CROSS(element))
3771 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3776 else if (element_info[element].explosion_type == EXPLODES_1X1)
3778 else if (CAN_EXPLODE_1X1(element))
3780 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3782 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3786 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3789 void SplashAcid(int x, int y)
3792 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3793 (!IN_LEV_FIELD(x - 1, y - 2) ||
3794 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3795 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3797 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3798 (!IN_LEV_FIELD(x + 1, y - 2) ||
3799 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3800 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3802 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3804 /* input: position of element entering acid (obsolete) */
3806 int element = Feld[x][y];
3808 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3811 if (element != EL_ACID_SPLASH_LEFT &&
3812 element != EL_ACID_SPLASH_RIGHT)
3814 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3816 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3817 (!IN_LEV_FIELD(x - 1, y - 1) ||
3818 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3819 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3821 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3822 (!IN_LEV_FIELD(x + 1, y - 1) ||
3823 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3824 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3829 static void InitBeltMovement()
3831 static int belt_base_element[4] =
3833 EL_CONVEYOR_BELT_1_LEFT,
3834 EL_CONVEYOR_BELT_2_LEFT,
3835 EL_CONVEYOR_BELT_3_LEFT,
3836 EL_CONVEYOR_BELT_4_LEFT
3838 static int belt_base_active_element[4] =
3840 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3841 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3842 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3843 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3848 /* set frame order for belt animation graphic according to belt direction */
3849 for (i = 0; i < NUM_BELTS; i++)
3853 for (j = 0; j < NUM_BELT_PARTS; j++)
3855 int element = belt_base_active_element[belt_nr] + j;
3856 int graphic = el2img(element);
3858 if (game.belt_dir[i] == MV_LEFT)
3859 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3861 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3865 for (y = 0; y < lev_fieldy; y++)
3867 for (x = 0; x < lev_fieldx; x++)
3869 int element = Feld[x][y];
3871 for (i = 0; i < NUM_BELTS; i++)
3873 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3875 int e_belt_nr = getBeltNrFromBeltElement(element);
3878 if (e_belt_nr == belt_nr)
3880 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3882 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3890 static void ToggleBeltSwitch(int x, int y)
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
3906 static int belt_base_switch_element[4] =
3908 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3909 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3910 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3911 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3913 static int belt_move_dir[4] =
3921 int element = Feld[x][y];
3922 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3923 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3924 int belt_dir = belt_move_dir[belt_dir_nr];
3927 if (!IS_BELT_SWITCH(element))
3930 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3931 game.belt_dir[belt_nr] = belt_dir;
3933 if (belt_dir_nr == 3)
3936 /* set frame order for belt animation graphic according to belt direction */
3937 for (i = 0; i < NUM_BELT_PARTS; i++)
3939 int element = belt_base_active_element[belt_nr] + i;
3940 int graphic = el2img(element);
3942 if (belt_dir == MV_LEFT)
3943 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3945 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3948 for (yy = 0; yy < lev_fieldy; yy++)
3950 for (xx = 0; xx < lev_fieldx; xx++)
3952 int element = Feld[xx][yy];
3954 if (IS_BELT_SWITCH(element))
3956 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3958 if (e_belt_nr == belt_nr)
3960 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3961 DrawLevelField(xx, yy);
3964 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3966 int e_belt_nr = getBeltNrFromBeltElement(element);
3968 if (e_belt_nr == belt_nr)
3970 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3972 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3973 DrawLevelField(xx, yy);
3976 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3978 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3980 if (e_belt_nr == belt_nr)
3982 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3984 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3985 DrawLevelField(xx, yy);
3992 static void ToggleSwitchgateSwitch(int x, int y)
3996 game.switchgate_pos = !game.switchgate_pos;
3998 for (yy = 0; yy < lev_fieldy; yy++)
4000 for (xx = 0; xx < lev_fieldx; xx++)
4002 int element = Feld[xx][yy];
4004 if (element == EL_SWITCHGATE_SWITCH_UP ||
4005 element == EL_SWITCHGATE_SWITCH_DOWN)
4007 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4008 DrawLevelField(xx, yy);
4010 else if (element == EL_SWITCHGATE_OPEN ||
4011 element == EL_SWITCHGATE_OPENING)
4013 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4015 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4017 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4020 else if (element == EL_SWITCHGATE_CLOSED ||
4021 element == EL_SWITCHGATE_CLOSING)
4023 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4025 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4027 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4034 static int getInvisibleActiveFromInvisibleElement(int element)
4036 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4037 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4038 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4042 static int getInvisibleFromInvisibleActiveElement(int element)
4044 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4045 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4046 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4050 static void RedrawAllLightSwitchesAndInvisibleElements()
4054 for (y = 0; y < lev_fieldy; y++)
4056 for (x = 0; x < lev_fieldx; x++)
4058 int element = Feld[x][y];
4060 if (element == EL_LIGHT_SWITCH &&
4061 game.light_time_left > 0)
4063 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4064 DrawLevelField(x, y);
4066 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4067 game.light_time_left == 0)
4069 Feld[x][y] = EL_LIGHT_SWITCH;
4070 DrawLevelField(x, y);
4072 else if (element == EL_INVISIBLE_STEELWALL ||
4073 element == EL_INVISIBLE_WALL ||
4074 element == EL_INVISIBLE_SAND)
4076 if (game.light_time_left > 0)
4077 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4079 DrawLevelField(x, y);
4081 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4082 element == EL_INVISIBLE_WALL_ACTIVE ||
4083 element == EL_INVISIBLE_SAND_ACTIVE)
4085 if (game.light_time_left == 0)
4086 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4088 DrawLevelField(x, y);
4094 static void ToggleLightSwitch(int x, int y)
4096 int element = Feld[x][y];
4098 game.light_time_left =
4099 (element == EL_LIGHT_SWITCH ?
4100 level.time_light * FRAMES_PER_SECOND : 0);
4102 RedrawAllLightSwitchesAndInvisibleElements();
4105 static void ActivateTimegateSwitch(int x, int y)
4109 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4111 for (yy = 0; yy < lev_fieldy; yy++)
4113 for (xx = 0; xx < lev_fieldx; xx++)
4115 int element = Feld[xx][yy];
4117 if (element == EL_TIMEGATE_CLOSED ||
4118 element == EL_TIMEGATE_CLOSING)
4120 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4121 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4125 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4127 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4128 DrawLevelField(xx, yy);
4135 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4138 inline static int getElementMoveStepsize(int x, int y)
4140 int element = Feld[x][y];
4141 int direction = MovDir[x][y];
4142 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4143 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4144 int horiz_move = (dx != 0);
4145 int sign = (horiz_move ? dx : dy);
4146 int step = sign * element_info[element].move_stepsize;
4148 /* special values for move stepsize for spring and things on conveyor belt */
4152 if (element == EL_SPRING)
4153 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4154 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4155 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4156 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4158 if (CAN_FALL(element) &&
4159 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4160 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4161 else if (element == EL_SPRING)
4162 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4169 void Impact(int x, int y)
4171 boolean lastline = (y == lev_fieldy-1);
4172 boolean object_hit = FALSE;
4173 boolean impact = (lastline || object_hit);
4174 int element = Feld[x][y];
4175 int smashed = EL_STEELWALL;
4177 if (!lastline) /* check if element below was hit */
4179 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4182 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4183 MovDir[x][y + 1] != MV_DOWN ||
4184 MovPos[x][y + 1] <= TILEY / 2));
4187 object_hit = !IS_FREE(x, y + 1);
4190 /* do not smash moving elements that left the smashed field in time */
4191 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4192 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4196 smashed = MovingOrBlocked2Element(x, y + 1);
4198 impact = (lastline || object_hit);
4201 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4203 SplashAcid(x, y + 1);
4207 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4208 /* only reset graphic animation if graphic really changes after impact */
4210 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4212 ResetGfxAnimation(x, y);
4213 DrawLevelField(x, y);
4216 if (impact && CAN_EXPLODE_IMPACT(element))
4221 else if (impact && element == EL_PEARL)
4223 ResetGfxAnimation(x, y);
4225 Feld[x][y] = EL_PEARL_BREAKING;
4226 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4229 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4231 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4236 if (impact && element == EL_AMOEBA_DROP)
4238 if (object_hit && IS_PLAYER(x, y + 1))
4239 KillHeroUnlessEnemyProtected(x, y + 1);
4240 else if (object_hit && smashed == EL_PENGUIN)
4244 Feld[x][y] = EL_AMOEBA_GROWING;
4245 Store[x][y] = EL_AMOEBA_WET;
4247 ResetRandomAnimationValue(x, y);
4252 if (object_hit) /* check which object was hit */
4254 if (CAN_PASS_MAGIC_WALL(element) &&
4255 (smashed == EL_MAGIC_WALL ||
4256 smashed == EL_BD_MAGIC_WALL))
4259 int activated_magic_wall =
4260 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4261 EL_BD_MAGIC_WALL_ACTIVE);
4263 /* activate magic wall / mill */
4264 for (yy = 0; yy < lev_fieldy; yy++)
4265 for (xx = 0; xx < lev_fieldx; xx++)
4266 if (Feld[xx][yy] == smashed)
4267 Feld[xx][yy] = activated_magic_wall;
4269 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4270 game.magic_wall_active = TRUE;
4272 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4273 SND_MAGIC_WALL_ACTIVATING :
4274 SND_BD_MAGIC_WALL_ACTIVATING));
4277 if (IS_PLAYER(x, y + 1))
4279 if (CAN_SMASH_PLAYER(element))
4281 KillHeroUnlessEnemyProtected(x, y + 1);
4285 else if (smashed == EL_PENGUIN)
4287 if (CAN_SMASH_PLAYER(element))
4293 else if (element == EL_BD_DIAMOND)
4295 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4301 else if (((element == EL_SP_INFOTRON ||
4302 element == EL_SP_ZONK) &&
4303 (smashed == EL_SP_SNIKSNAK ||
4304 smashed == EL_SP_ELECTRON ||
4305 smashed == EL_SP_DISK_ORANGE)) ||
4306 (element == EL_SP_INFOTRON &&
4307 smashed == EL_SP_DISK_YELLOW))
4313 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4319 else if (CAN_SMASH_EVERYTHING(element))
4321 if (IS_CLASSIC_ENEMY(smashed) ||
4322 CAN_EXPLODE_SMASHED(smashed))
4327 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4329 if (smashed == EL_LAMP ||
4330 smashed == EL_LAMP_ACTIVE)
4335 else if (smashed == EL_NUT)
4337 Feld[x][y + 1] = EL_NUT_BREAKING;
4338 PlayLevelSound(x, y, SND_NUT_BREAKING);
4339 RaiseScoreElement(EL_NUT);
4342 else if (smashed == EL_PEARL)
4344 ResetGfxAnimation(x, y);
4346 Feld[x][y + 1] = EL_PEARL_BREAKING;
4347 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4350 else if (smashed == EL_DIAMOND)
4352 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4353 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4356 else if (IS_BELT_SWITCH(smashed))
4358 ToggleBeltSwitch(x, y + 1);
4360 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4361 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4363 ToggleSwitchgateSwitch(x, y + 1);
4365 else if (smashed == EL_LIGHT_SWITCH ||
4366 smashed == EL_LIGHT_SWITCH_ACTIVE)
4368 ToggleLightSwitch(x, y + 1);
4373 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4376 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4379 /* !!! TEST ONLY !!! */
4380 CheckElementChangeBySide(x, y + 1, smashed, element,
4381 CE_SWITCHED, CH_SIDE_TOP);
4382 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4383 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4385 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4386 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4387 CheckElementChangeBySide(x, y + 1, smashed, element,
4388 CE_SWITCHED, CH_SIDE_TOP);
4394 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4399 /* play sound of magic wall / mill */
4401 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4402 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4404 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4405 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4406 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4407 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4412 /* play sound of object that hits the ground */
4413 if (lastline || object_hit)
4414 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4417 inline static void TurnRoundExt(int x, int y)
4429 { 0, 0 }, { 0, 0 }, { 0, 0 },
4434 int left, right, back;
4438 { MV_DOWN, MV_UP, MV_RIGHT },
4439 { MV_UP, MV_DOWN, MV_LEFT },
4441 { MV_LEFT, MV_RIGHT, MV_DOWN },
4445 { MV_RIGHT, MV_LEFT, MV_UP }
4448 int element = Feld[x][y];
4449 int move_pattern = element_info[element].move_pattern;
4451 int old_move_dir = MovDir[x][y];
4452 int left_dir = turn[old_move_dir].left;
4453 int right_dir = turn[old_move_dir].right;
4454 int back_dir = turn[old_move_dir].back;
4456 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4457 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4458 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4459 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4461 int left_x = x + left_dx, left_y = y + left_dy;
4462 int right_x = x + right_dx, right_y = y + right_dy;
4463 int move_x = x + move_dx, move_y = y + move_dy;
4467 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4469 TestIfBadThingTouchesOtherBadThing(x, y);
4471 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4472 MovDir[x][y] = right_dir;
4473 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4474 MovDir[x][y] = left_dir;
4476 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4478 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4482 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4483 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4485 TestIfBadThingTouchesOtherBadThing(x, y);
4487 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4488 MovDir[x][y] = left_dir;
4489 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4490 MovDir[x][y] = right_dir;
4492 if ((element == EL_SPACESHIP ||
4493 element == EL_SP_SNIKSNAK ||
4494 element == EL_SP_ELECTRON)
4495 && MovDir[x][y] != old_move_dir)
4497 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4501 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4503 TestIfBadThingTouchesOtherBadThing(x, y);
4505 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4506 MovDir[x][y] = left_dir;
4507 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4508 MovDir[x][y] = right_dir;
4510 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4512 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4515 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4517 TestIfBadThingTouchesOtherBadThing(x, y);
4519 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4520 MovDir[x][y] = left_dir;
4521 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4522 MovDir[x][y] = right_dir;
4524 if (MovDir[x][y] != old_move_dir)
4528 else if (element == EL_YAMYAM)
4530 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4531 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4533 if (can_turn_left && can_turn_right)
4534 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4535 else if (can_turn_left)
4536 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4537 else if (can_turn_right)
4538 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4540 MovDir[x][y] = back_dir;
4542 MovDelay[x][y] = 16 + 16 * RND(3);
4544 else if (element == EL_DARK_YAMYAM)
4546 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4548 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4551 if (can_turn_left && can_turn_right)
4552 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4553 else if (can_turn_left)
4554 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4555 else if (can_turn_right)
4556 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4558 MovDir[x][y] = back_dir;
4560 MovDelay[x][y] = 16 + 16 * RND(3);
4562 else if (element == EL_PACMAN)
4564 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4565 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4567 if (can_turn_left && can_turn_right)
4568 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4569 else if (can_turn_left)
4570 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4571 else if (can_turn_right)
4572 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4574 MovDir[x][y] = back_dir;
4576 MovDelay[x][y] = 6 + RND(40);
4578 else if (element == EL_PIG)
4580 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4581 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4582 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4583 boolean should_turn_left, should_turn_right, should_move_on;
4585 int rnd = RND(rnd_value);
4587 should_turn_left = (can_turn_left &&
4589 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4590 y + back_dy + left_dy)));
4591 should_turn_right = (can_turn_right &&
4593 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4594 y + back_dy + right_dy)));
4595 should_move_on = (can_move_on &&
4598 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4599 y + move_dy + left_dy) ||
4600 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4601 y + move_dy + right_dy)));
4603 if (should_turn_left || should_turn_right || should_move_on)
4605 if (should_turn_left && should_turn_right && should_move_on)
4606 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4607 rnd < 2 * rnd_value / 3 ? right_dir :
4609 else if (should_turn_left && should_turn_right)
4610 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4611 else if (should_turn_left && should_move_on)
4612 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4613 else if (should_turn_right && should_move_on)
4614 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4615 else if (should_turn_left)
4616 MovDir[x][y] = left_dir;
4617 else if (should_turn_right)
4618 MovDir[x][y] = right_dir;
4619 else if (should_move_on)
4620 MovDir[x][y] = old_move_dir;
4622 else if (can_move_on && rnd > rnd_value / 8)
4623 MovDir[x][y] = old_move_dir;
4624 else if (can_turn_left && can_turn_right)
4625 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4626 else if (can_turn_left && rnd > rnd_value / 8)
4627 MovDir[x][y] = left_dir;
4628 else if (can_turn_right && rnd > rnd_value/8)
4629 MovDir[x][y] = right_dir;
4631 MovDir[x][y] = back_dir;
4633 xx = x + move_xy[MovDir[x][y]].x;
4634 yy = y + move_xy[MovDir[x][y]].y;
4636 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4637 MovDir[x][y] = old_move_dir;
4641 else if (element == EL_DRAGON)
4643 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4644 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4645 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4647 int rnd = RND(rnd_value);
4650 if (FrameCounter < 1 && x == 0 && y == 29)
4651 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4654 if (can_move_on && rnd > rnd_value / 8)
4655 MovDir[x][y] = old_move_dir;
4656 else if (can_turn_left && can_turn_right)
4657 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4658 else if (can_turn_left && rnd > rnd_value / 8)
4659 MovDir[x][y] = left_dir;
4660 else if (can_turn_right && rnd > rnd_value / 8)
4661 MovDir[x][y] = right_dir;
4663 MovDir[x][y] = back_dir;
4665 xx = x + move_xy[MovDir[x][y]].x;
4666 yy = y + move_xy[MovDir[x][y]].y;
4669 if (FrameCounter < 1 && x == 0 && y == 29)
4670 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4671 xx, yy, Feld[xx][yy],
4676 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4677 MovDir[x][y] = old_move_dir;
4679 if (!IS_FREE(xx, yy))
4680 MovDir[x][y] = old_move_dir;
4684 if (FrameCounter < 1 && x == 0 && y == 29)
4685 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4690 else if (element == EL_MOLE)
4692 boolean can_move_on =
4693 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4694 IS_AMOEBOID(Feld[move_x][move_y]) ||
4695 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4698 boolean can_turn_left =
4699 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4700 IS_AMOEBOID(Feld[left_x][left_y])));
4702 boolean can_turn_right =
4703 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4704 IS_AMOEBOID(Feld[right_x][right_y])));
4706 if (can_turn_left && can_turn_right)
4707 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4708 else if (can_turn_left)
4709 MovDir[x][y] = left_dir;
4711 MovDir[x][y] = right_dir;
4714 if (MovDir[x][y] != old_move_dir)
4717 else if (element == EL_BALLOON)
4719 MovDir[x][y] = game.balloon_dir;
4722 else if (element == EL_SPRING)
4725 if (MovDir[x][y] & MV_HORIZONTAL &&
4726 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4727 MovDir[x][y] = MV_NO_MOVING;
4729 if (MovDir[x][y] & MV_HORIZONTAL &&
4730 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4731 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4732 MovDir[x][y] = MV_NO_MOVING;
4737 else if (element == EL_ROBOT ||
4738 element == EL_SATELLITE ||
4739 element == EL_PENGUIN)
4741 int attr_x = -1, attr_y = -1;
4752 for (i = 0; i < MAX_PLAYERS; i++)
4754 struct PlayerInfo *player = &stored_player[i];
4755 int jx = player->jx, jy = player->jy;
4757 if (!player->active)
4761 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4770 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4771 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4772 game.engine_version < VERSION_IDENT(3,1,0,0)))
4774 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4781 if (element == EL_PENGUIN)
4784 static int xy[4][2] =
4792 for (i = 0; i < NUM_DIRECTIONS; i++)
4794 int ex = x + xy[i][0];
4795 int ey = y + xy[i][1];
4797 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4806 MovDir[x][y] = MV_NO_MOVING;
4808 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4809 else if (attr_x > x)
4810 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4812 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4813 else if (attr_y > y)
4814 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4816 if (element == EL_ROBOT)
4820 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4821 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4822 Moving2Blocked(x, y, &newx, &newy);
4824 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4825 MovDelay[x][y] = 8 + 8 * !RND(3);
4827 MovDelay[x][y] = 16;
4829 else if (element == EL_PENGUIN)
4835 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4837 boolean first_horiz = RND(2);
4838 int new_move_dir = MovDir[x][y];
4841 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4842 Moving2Blocked(x, y, &newx, &newy);
4844 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4848 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4849 Moving2Blocked(x, y, &newx, &newy);
4851 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4854 MovDir[x][y] = old_move_dir;
4858 else /* (element == EL_SATELLITE) */
4864 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4866 boolean first_horiz = RND(2);
4867 int new_move_dir = MovDir[x][y];
4870 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4871 Moving2Blocked(x, y, &newx, &newy);
4873 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4877 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4878 Moving2Blocked(x, y, &newx, &newy);
4880 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4883 MovDir[x][y] = old_move_dir;
4888 else if (move_pattern == MV_TURNING_LEFT ||
4889 move_pattern == MV_TURNING_RIGHT ||
4890 move_pattern == MV_TURNING_LEFT_RIGHT ||
4891 move_pattern == MV_TURNING_RIGHT_LEFT ||
4892 move_pattern == MV_TURNING_RANDOM ||
4893 move_pattern == MV_ALL_DIRECTIONS)
4895 boolean can_turn_left =
4896 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4897 boolean can_turn_right =
4898 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4900 if (move_pattern == MV_TURNING_LEFT)
4901 MovDir[x][y] = left_dir;
4902 else if (move_pattern == MV_TURNING_RIGHT)
4903 MovDir[x][y] = right_dir;
4904 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4905 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4906 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4907 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4908 else if (move_pattern == MV_TURNING_RANDOM)
4909 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4910 can_turn_right && !can_turn_left ? right_dir :
4911 RND(2) ? left_dir : right_dir);
4912 else if (can_turn_left && can_turn_right)
4913 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4914 else if (can_turn_left)
4915 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4916 else if (can_turn_right)
4917 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4919 MovDir[x][y] = back_dir;
4921 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4923 else if (move_pattern == MV_HORIZONTAL ||
4924 move_pattern == MV_VERTICAL)
4926 if (move_pattern & old_move_dir)
4927 MovDir[x][y] = back_dir;
4928 else if (move_pattern == MV_HORIZONTAL)
4929 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4930 else if (move_pattern == MV_VERTICAL)
4931 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4933 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4935 else if (move_pattern & MV_ANY_DIRECTION)
4937 MovDir[x][y] = move_pattern;
4938 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4940 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4942 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4943 MovDir[x][y] = left_dir;
4944 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4945 MovDir[x][y] = right_dir;
4947 if (MovDir[x][y] != old_move_dir)
4948 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4950 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4952 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4953 MovDir[x][y] = right_dir;
4954 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4955 MovDir[x][y] = left_dir;
4957 if (MovDir[x][y] != old_move_dir)
4958 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4960 else if (move_pattern == MV_TOWARDS_PLAYER ||
4961 move_pattern == MV_AWAY_FROM_PLAYER)
4963 int attr_x = -1, attr_y = -1;
4965 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4976 for (i = 0; i < MAX_PLAYERS; i++)
4978 struct PlayerInfo *player = &stored_player[i];
4979 int jx = player->jx, jy = player->jy;
4981 if (!player->active)
4985 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4993 MovDir[x][y] = MV_NO_MOVING;
4995 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4996 else if (attr_x > x)
4997 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4999 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5000 else if (attr_y > y)
5001 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5003 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5005 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5007 boolean first_horiz = RND(2);
5008 int new_move_dir = MovDir[x][y];
5011 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5012 Moving2Blocked(x, y, &newx, &newy);
5014 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5018 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5019 Moving2Blocked(x, y, &newx, &newy);
5021 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5024 MovDir[x][y] = old_move_dir;
5027 else if (move_pattern == MV_WHEN_PUSHED ||
5028 move_pattern == MV_WHEN_DROPPED)
5030 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5031 MovDir[x][y] = MV_NO_MOVING;
5035 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5037 static int test_xy[7][2] =
5047 static int test_dir[7] =
5057 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5058 int move_preference = -1000000; /* start with very low preference */
5059 int new_move_dir = MV_NO_MOVING;
5060 int start_test = RND(4);
5063 for (i = 0; i < NUM_DIRECTIONS; i++)
5065 int move_dir = test_dir[start_test + i];
5066 int move_dir_preference;
5068 xx = x + test_xy[start_test + i][0];
5069 yy = y + test_xy[start_test + i][1];
5071 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5072 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5074 new_move_dir = move_dir;
5079 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5082 move_dir_preference = -1 * RunnerVisit[xx][yy];
5083 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5084 move_dir_preference = PlayerVisit[xx][yy];
5086 if (move_dir_preference > move_preference)
5088 /* prefer field that has not been visited for the longest time */
5089 move_preference = move_dir_preference;
5090 new_move_dir = move_dir;
5092 else if (move_dir_preference == move_preference &&
5093 move_dir == old_move_dir)
5095 /* prefer last direction when all directions are preferred equally */
5096 move_preference = move_dir_preference;
5097 new_move_dir = move_dir;
5101 MovDir[x][y] = new_move_dir;
5102 if (old_move_dir != new_move_dir)
5107 static void TurnRound(int x, int y)
5109 int direction = MovDir[x][y];
5112 GfxDir[x][y] = MovDir[x][y];
5118 GfxDir[x][y] = MovDir[x][y];
5121 if (direction != MovDir[x][y])
5126 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5129 GfxAction[x][y] = ACTION_WAITING;
5133 static boolean JustBeingPushed(int x, int y)
5137 for (i = 0; i < MAX_PLAYERS; i++)
5139 struct PlayerInfo *player = &stored_player[i];
5141 if (player->active && player->is_pushing && player->MovPos)
5143 int next_jx = player->jx + (player->jx - player->last_jx);
5144 int next_jy = player->jy + (player->jy - player->last_jy);
5146 if (x == next_jx && y == next_jy)
5154 void StartMoving(int x, int y)
5157 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5159 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5160 int element = Feld[x][y];
5166 if (MovDelay[x][y] == 0)
5167 GfxAction[x][y] = ACTION_DEFAULT;
5169 /* !!! this should be handled more generic (not only for mole) !!! */
5170 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5171 GfxAction[x][y] = ACTION_DEFAULT;
5174 if (CAN_FALL(element) && y < lev_fieldy - 1)
5176 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5177 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5178 if (JustBeingPushed(x, y))
5181 if (element == EL_QUICKSAND_FULL)
5183 if (IS_FREE(x, y + 1))
5185 InitMovingField(x, y, MV_DOWN);
5186 started_moving = TRUE;
5188 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5189 Store[x][y] = EL_ROCK;
5191 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5193 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5196 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5198 if (!MovDelay[x][y])
5199 MovDelay[x][y] = TILEY + 1;
5208 Feld[x][y] = EL_QUICKSAND_EMPTY;
5209 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5210 Store[x][y + 1] = Store[x][y];
5213 PlayLevelSoundAction(x, y, ACTION_FILLING);
5215 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5219 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5220 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5222 InitMovingField(x, y, MV_DOWN);
5223 started_moving = TRUE;
5225 Feld[x][y] = EL_QUICKSAND_FILLING;
5226 Store[x][y] = element;
5228 PlayLevelSoundAction(x, y, ACTION_FILLING);
5230 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5233 else if (element == EL_MAGIC_WALL_FULL)
5235 if (IS_FREE(x, y + 1))
5237 InitMovingField(x, y, MV_DOWN);
5238 started_moving = TRUE;
5240 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5241 Store[x][y] = EL_CHANGED(Store[x][y]);
5243 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5245 if (!MovDelay[x][y])
5246 MovDelay[x][y] = TILEY/4 + 1;
5255 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5256 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5257 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5261 else if (element == EL_BD_MAGIC_WALL_FULL)
5263 if (IS_FREE(x, y + 1))
5265 InitMovingField(x, y, MV_DOWN);
5266 started_moving = TRUE;
5268 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5269 Store[x][y] = EL_CHANGED2(Store[x][y]);
5271 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5273 if (!MovDelay[x][y])
5274 MovDelay[x][y] = TILEY/4 + 1;
5283 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5284 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5285 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5289 else if (CAN_PASS_MAGIC_WALL(element) &&
5290 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5291 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5293 InitMovingField(x, y, MV_DOWN);
5294 started_moving = TRUE;
5297 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5298 EL_BD_MAGIC_WALL_FILLING);
5299 Store[x][y] = element;
5302 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5304 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5307 SplashAcid(x, y + 1);
5309 InitMovingField(x, y, MV_DOWN);
5310 started_moving = TRUE;
5312 Store[x][y] = EL_ACID;
5314 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5315 GfxAction[x][y + 1] = ACTION_ACTIVE;
5319 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5320 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5322 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5323 CAN_SMASH(element) && WasJustFalling[x][y] &&
5324 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5326 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5327 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5328 (Feld[x][y + 1] == EL_BLOCKED)))
5332 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5333 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5334 WasJustMoving[x][y] && !Pushed[x][y + 1])
5336 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5337 WasJustMoving[x][y])
5342 /* this is needed for a special case not covered by calling "Impact()"
5343 from "ContinueMoving()": if an element moves to a tile directly below
5344 another element which was just falling on that tile (which was empty
5345 in the previous frame), the falling element above would just stop
5346 instead of smashing the element below (in previous version, the above
5347 element was just checked for "moving" instead of "falling", resulting
5348 in incorrect smashes caused by horizontal movement of the above
5349 element; also, the case of the player being the element to smash was
5350 simply not covered here... :-/ ) */
5353 WasJustMoving[x][y] = 0;
5354 WasJustFalling[x][y] = 0;
5357 CheckCollision[x][y] = 0;
5360 if (IS_PLAYER(x, y + 1))
5361 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5366 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5368 if (MovDir[x][y] == MV_NO_MOVING)
5370 InitMovingField(x, y, MV_DOWN);
5371 started_moving = TRUE;
5374 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5376 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5377 MovDir[x][y] = MV_DOWN;
5379 InitMovingField(x, y, MV_DOWN);
5380 started_moving = TRUE;
5382 else if (element == EL_AMOEBA_DROP)
5384 Feld[x][y] = EL_AMOEBA_GROWING;
5385 Store[x][y] = EL_AMOEBA_WET;
5387 /* Store[x][y + 1] must be zero, because:
5388 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5391 #if OLD_GAME_BEHAVIOUR
5392 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5394 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5395 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5396 element != EL_DX_SUPABOMB)
5399 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5400 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5401 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5402 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5405 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5406 (IS_FREE(x - 1, y + 1) ||
5407 Feld[x - 1][y + 1] == EL_ACID));
5408 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5409 (IS_FREE(x + 1, y + 1) ||
5410 Feld[x + 1][y + 1] == EL_ACID));
5411 boolean can_fall_any = (can_fall_left || can_fall_right);
5412 boolean can_fall_both = (can_fall_left && can_fall_right);
5414 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5416 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5418 if (slippery_type == SLIPPERY_ONLY_LEFT)
5419 can_fall_right = FALSE;
5420 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5421 can_fall_left = FALSE;
5422 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5423 can_fall_right = FALSE;
5424 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5425 can_fall_left = FALSE;
5427 can_fall_any = (can_fall_left || can_fall_right);
5428 can_fall_both = (can_fall_left && can_fall_right);
5433 if (can_fall_both &&
5434 (game.emulation != EMU_BOULDERDASH &&
5435 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5436 can_fall_left = !(can_fall_right = RND(2));
5438 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5439 started_moving = TRUE;
5443 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5445 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5448 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5449 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5450 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5451 int belt_dir = game.belt_dir[belt_nr];
5453 if ((belt_dir == MV_LEFT && left_is_free) ||
5454 (belt_dir == MV_RIGHT && right_is_free))
5457 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5460 InitMovingField(x, y, belt_dir);
5461 started_moving = TRUE;
5464 Pushed[x][y] = TRUE;
5465 Pushed[nextx][y] = TRUE;
5468 GfxAction[x][y] = ACTION_DEFAULT;
5472 MovDir[x][y] = 0; /* if element was moving, stop it */
5477 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5479 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5481 if (CAN_MOVE(element) && !started_moving)
5484 int move_pattern = element_info[element].move_pattern;
5489 if (MovDir[x][y] == MV_NO_MOVING)
5491 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5492 x, y, element, element_info[element].token_name);
5493 printf("StartMoving(): This should never happen!\n");
5498 Moving2Blocked(x, y, &newx, &newy);
5501 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5504 if ((element == EL_SATELLITE ||
5505 element == EL_BALLOON ||
5506 element == EL_SPRING)
5507 && JustBeingPushed(x, y))
5514 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5515 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5517 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5518 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5519 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5523 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5524 element, element_info[element].token_name,
5525 WasJustMoving[x][y],
5526 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5527 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5528 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5529 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5533 WasJustMoving[x][y] = 0;
5536 CheckCollision[x][y] = 0;
5538 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5541 if (Feld[x][y] != element) /* element has changed */
5543 element = Feld[x][y];
5544 move_pattern = element_info[element].move_pattern;
5546 if (!CAN_MOVE(element))
5550 if (Feld[x][y] != element) /* element has changed */
5558 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5559 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5561 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5563 Moving2Blocked(x, y, &newx, &newy);
5564 if (Feld[newx][newy] == EL_BLOCKED)
5565 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5571 if (FrameCounter < 1 && x == 0 && y == 29)
5572 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5575 if (!MovDelay[x][y]) /* start new movement phase */
5577 /* all objects that can change their move direction after each step
5578 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5580 if (element != EL_YAMYAM &&
5581 element != EL_DARK_YAMYAM &&
5582 element != EL_PACMAN &&
5583 !(move_pattern & MV_ANY_DIRECTION) &&
5584 move_pattern != MV_TURNING_LEFT &&
5585 move_pattern != MV_TURNING_RIGHT &&
5586 move_pattern != MV_TURNING_LEFT_RIGHT &&
5587 move_pattern != MV_TURNING_RIGHT_LEFT &&
5588 move_pattern != MV_TURNING_RANDOM)
5593 if (FrameCounter < 1 && x == 0 && y == 29)
5594 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5597 if (MovDelay[x][y] && (element == EL_BUG ||
5598 element == EL_SPACESHIP ||
5599 element == EL_SP_SNIKSNAK ||
5600 element == EL_SP_ELECTRON ||
5601 element == EL_MOLE))
5602 DrawLevelField(x, y);
5606 if (MovDelay[x][y]) /* wait some time before next movement */
5611 if (element == EL_YAMYAM)
5614 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5615 DrawLevelElementAnimation(x, y, element);
5619 if (MovDelay[x][y]) /* element still has to wait some time */
5622 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5623 ResetGfxAnimation(x, y);
5627 if (GfxAction[x][y] != ACTION_WAITING)
5628 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5630 GfxAction[x][y] = ACTION_WAITING;
5634 if (element == EL_ROBOT ||
5636 element == EL_PACMAN ||
5638 element == EL_YAMYAM ||
5639 element == EL_DARK_YAMYAM)
5642 DrawLevelElementAnimation(x, y, element);
5644 DrawLevelElementAnimationIfNeeded(x, y, element);
5646 PlayLevelSoundAction(x, y, ACTION_WAITING);
5648 else if (element == EL_SP_ELECTRON)
5649 DrawLevelElementAnimationIfNeeded(x, y, element);
5650 else if (element == EL_DRAGON)
5653 int dir = MovDir[x][y];
5654 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5655 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5656 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5657 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5658 dir == MV_UP ? IMG_FLAMES_1_UP :
5659 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5660 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5663 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5666 GfxAction[x][y] = ACTION_ATTACKING;
5668 if (IS_PLAYER(x, y))
5669 DrawPlayerField(x, y);
5671 DrawLevelField(x, y);
5673 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5675 for (i = 1; i <= 3; i++)
5677 int xx = x + i * dx;
5678 int yy = y + i * dy;
5679 int sx = SCREENX(xx);
5680 int sy = SCREENY(yy);
5681 int flame_graphic = graphic + (i - 1);
5683 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5688 int flamed = MovingOrBlocked2Element(xx, yy);
5692 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5694 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5695 RemoveMovingField(xx, yy);
5697 RemoveField(xx, yy);
5699 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5702 RemoveMovingField(xx, yy);
5706 if (ChangeDelay[xx][yy])
5707 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5708 Feld[xx][yy] == EL_BLOCKED));
5712 ChangeDelay[xx][yy] = 0;
5714 Feld[xx][yy] = EL_FLAMES;
5715 if (IN_SCR_FIELD(sx, sy))
5717 DrawLevelFieldCrumbledSand(xx, yy);
5718 DrawGraphic(sx, sy, flame_graphic, frame);
5723 if (Feld[xx][yy] == EL_FLAMES)
5724 Feld[xx][yy] = EL_EMPTY;
5725 DrawLevelField(xx, yy);
5730 if (MovDelay[x][y]) /* element still has to wait some time */
5732 PlayLevelSoundAction(x, y, ACTION_WAITING);
5738 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5739 for all other elements GfxAction will be set by InitMovingField() */
5740 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5741 GfxAction[x][y] = ACTION_MOVING;
5745 /* now make next step */
5747 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5749 if (DONT_COLLIDE_WITH(element) &&
5750 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5751 !PLAYER_ENEMY_PROTECTED(newx, newy))
5754 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5758 /* player killed by element which is deadly when colliding with */
5760 KillHero(PLAYERINFO(newx, newy));
5767 else if (CAN_MOVE_INTO_ACID(element) &&
5768 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5769 (MovDir[x][y] == MV_DOWN ||
5770 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5772 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5773 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5777 else if ((element == EL_PENGUIN ||
5778 element == EL_ROBOT ||
5779 element == EL_SATELLITE ||
5780 element == EL_BALLOON ||
5781 IS_CUSTOM_ELEMENT(element)) &&
5782 IN_LEV_FIELD(newx, newy) &&
5783 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5786 SplashAcid(newx, newy);
5787 Store[x][y] = EL_ACID;
5789 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5791 if (Feld[newx][newy] == EL_EXIT_OPEN)
5795 DrawLevelField(x, y);
5797 Feld[x][y] = EL_EMPTY;
5798 DrawLevelField(x, y);
5801 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5802 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5803 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5805 local_player->friends_still_needed--;
5806 if (!local_player->friends_still_needed &&
5807 !local_player->GameOver && AllPlayersGone)
5808 local_player->LevelSolved = local_player->GameOver = TRUE;
5812 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5814 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5815 DrawLevelField(newx, newy);
5817 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5819 else if (!IS_FREE(newx, newy))
5821 GfxAction[x][y] = ACTION_WAITING;
5823 if (IS_PLAYER(x, y))
5824 DrawPlayerField(x, y);
5826 DrawLevelField(x, y);
5831 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5833 if (IS_FOOD_PIG(Feld[newx][newy]))
5835 if (IS_MOVING(newx, newy))
5836 RemoveMovingField(newx, newy);
5839 Feld[newx][newy] = EL_EMPTY;
5840 DrawLevelField(newx, newy);
5843 PlayLevelSound(x, y, SND_PIG_DIGGING);
5845 else if (!IS_FREE(newx, newy))
5847 if (IS_PLAYER(x, y))
5848 DrawPlayerField(x, y);
5850 DrawLevelField(x, y);
5859 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5862 else if (IS_CUSTOM_ELEMENT(element) &&
5863 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5867 !IS_FREE(newx, newy)
5872 int new_element = Feld[newx][newy];
5875 printf("::: '%s' digs '%s' [%d]\n",
5876 element_info[element].token_name,
5877 element_info[Feld[newx][newy]].token_name,
5878 StorePlayer[newx][newy]);
5881 if (!IS_FREE(newx, newy))
5883 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5884 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5887 /* no element can dig solid indestructible elements */
5888 if (IS_INDESTRUCTIBLE(new_element) &&
5889 !IS_DIGGABLE(new_element) &&
5890 !IS_COLLECTIBLE(new_element))
5893 if (AmoebaNr[newx][newy] &&
5894 (new_element == EL_AMOEBA_FULL ||
5895 new_element == EL_BD_AMOEBA ||
5896 new_element == EL_AMOEBA_GROWING))
5898 AmoebaCnt[AmoebaNr[newx][newy]]--;
5899 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5902 if (IS_MOVING(newx, newy))
5903 RemoveMovingField(newx, newy);
5906 RemoveField(newx, newy);
5907 DrawLevelField(newx, newy);
5910 /* if digged element was about to explode, prevent the explosion */
5911 ExplodeField[newx][newy] = EX_TYPE_NONE;
5913 PlayLevelSoundAction(x, y, action);
5918 Store[newx][newy] = EL_EMPTY;
5919 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5920 Store[newx][newy] = element_info[element].move_leave_element;
5922 Store[newx][newy] = EL_EMPTY;
5923 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5924 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5925 Store[newx][newy] = element_info[element].move_leave_element;
5928 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5929 element_info[element].can_leave_element = TRUE;
5932 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5934 RunnerVisit[x][y] = FrameCounter;
5935 PlayerVisit[x][y] /= 8; /* expire player visit path */
5941 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5943 if (!IS_FREE(newx, newy))
5945 if (IS_PLAYER(x, y))
5946 DrawPlayerField(x, y);
5948 DrawLevelField(x, y);
5954 boolean wanna_flame = !RND(10);
5955 int dx = newx - x, dy = newy - y;
5956 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5957 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5958 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5959 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5960 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5961 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5964 IS_CLASSIC_ENEMY(element1) ||
5965 IS_CLASSIC_ENEMY(element2)) &&
5966 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5967 element1 != EL_FLAMES && element2 != EL_FLAMES)
5970 ResetGfxAnimation(x, y);
5971 GfxAction[x][y] = ACTION_ATTACKING;
5974 if (IS_PLAYER(x, y))
5975 DrawPlayerField(x, y);
5977 DrawLevelField(x, y);
5979 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5981 MovDelay[x][y] = 50;
5985 RemoveField(newx, newy);
5987 Feld[newx][newy] = EL_FLAMES;
5988 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5991 RemoveField(newx1, newy1);
5993 Feld[newx1][newy1] = EL_FLAMES;
5995 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5998 RemoveField(newx2, newy2);
6000 Feld[newx2][newy2] = EL_FLAMES;
6007 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6008 Feld[newx][newy] == EL_DIAMOND)
6010 if (IS_MOVING(newx, newy))
6011 RemoveMovingField(newx, newy);
6014 Feld[newx][newy] = EL_EMPTY;
6015 DrawLevelField(newx, newy);
6018 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6020 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6021 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6023 if (AmoebaNr[newx][newy])
6025 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6026 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6027 Feld[newx][newy] == EL_BD_AMOEBA)
6028 AmoebaCnt[AmoebaNr[newx][newy]]--;
6033 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6035 if (IS_MOVING(newx, newy))
6038 RemoveMovingField(newx, newy);
6042 Feld[newx][newy] = EL_EMPTY;
6043 DrawLevelField(newx, newy);
6046 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6048 else if ((element == EL_PACMAN || element == EL_MOLE)
6049 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6051 if (AmoebaNr[newx][newy])
6053 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6054 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6055 Feld[newx][newy] == EL_BD_AMOEBA)
6056 AmoebaCnt[AmoebaNr[newx][newy]]--;
6059 if (element == EL_MOLE)
6061 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6062 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6064 ResetGfxAnimation(x, y);
6065 GfxAction[x][y] = ACTION_DIGGING;
6066 DrawLevelField(x, y);
6068 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6070 return; /* wait for shrinking amoeba */
6072 else /* element == EL_PACMAN */
6074 Feld[newx][newy] = EL_EMPTY;
6075 DrawLevelField(newx, newy);
6076 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6079 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6080 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6081 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6083 /* wait for shrinking amoeba to completely disappear */
6086 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6088 /* object was running against a wall */
6093 if (move_pattern & MV_ANY_DIRECTION &&
6094 move_pattern == MovDir[x][y])
6096 int blocking_element =
6097 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6100 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6101 element_info[element].token_name,
6102 element_info[blocking_element].token_name,
6106 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6109 element = Feld[x][y]; /* element might have changed */
6114 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6115 DrawLevelElementAnimation(x, y, element);
6117 if (element == EL_BUG ||
6118 element == EL_SPACESHIP ||
6119 element == EL_SP_SNIKSNAK)
6120 DrawLevelField(x, y);
6121 else if (element == EL_MOLE)
6122 DrawLevelField(x, y);
6123 else if (element == EL_BD_BUTTERFLY ||
6124 element == EL_BD_FIREFLY)
6125 DrawLevelElementAnimationIfNeeded(x, y, element);
6126 else if (element == EL_SATELLITE)
6127 DrawLevelElementAnimationIfNeeded(x, y, element);
6128 else if (element == EL_SP_ELECTRON)
6129 DrawLevelElementAnimationIfNeeded(x, y, element);
6132 if (DONT_TOUCH(element))
6133 TestIfBadThingTouchesHero(x, y);
6136 PlayLevelSoundAction(x, y, ACTION_WAITING);
6142 InitMovingField(x, y, MovDir[x][y]);
6144 PlayLevelSoundAction(x, y, ACTION_MOVING);
6148 ContinueMoving(x, y);
6151 void ContinueMoving(int x, int y)
6153 int element = Feld[x][y];
6154 int stored = Store[x][y];
6155 struct ElementInfo *ei = &element_info[element];
6156 int direction = MovDir[x][y];
6157 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6158 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6159 int newx = x + dx, newy = y + dy;
6161 int nextx = newx + dx, nexty = newy + dy;
6164 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6165 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6167 boolean pushed_by_player = Pushed[x][y];
6170 MovPos[x][y] += getElementMoveStepsize(x, y);
6173 if (pushed_by_player && IS_PLAYER(x, y))
6175 /* special case: moving object pushed by player */
6176 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6179 if (pushed_by_player) /* special case: moving object pushed by player */
6180 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6183 if (ABS(MovPos[x][y]) < TILEX)
6185 DrawLevelField(x, y);
6187 return; /* element is still moving */
6190 /* element reached destination field */
6192 Feld[x][y] = EL_EMPTY;
6193 Feld[newx][newy] = element;
6194 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6197 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6199 element = Feld[newx][newy] = EL_ACID;
6202 else if (element == EL_MOLE)
6204 Feld[x][y] = EL_SAND;
6206 DrawLevelFieldCrumbledSandNeighbours(x, y);
6208 else if (element == EL_QUICKSAND_FILLING)
6210 element = Feld[newx][newy] = get_next_element(element);
6211 Store[newx][newy] = Store[x][y];
6213 else if (element == EL_QUICKSAND_EMPTYING)
6215 Feld[x][y] = get_next_element(element);
6216 element = Feld[newx][newy] = Store[x][y];
6218 else if (element == EL_MAGIC_WALL_FILLING)
6220 element = Feld[newx][newy] = get_next_element(element);
6221 if (!game.magic_wall_active)
6222 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6223 Store[newx][newy] = Store[x][y];
6225 else if (element == EL_MAGIC_WALL_EMPTYING)
6227 Feld[x][y] = get_next_element(element);
6228 if (!game.magic_wall_active)
6229 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6230 element = Feld[newx][newy] = Store[x][y];
6232 else if (element == EL_BD_MAGIC_WALL_FILLING)
6234 element = Feld[newx][newy] = get_next_element(element);
6235 if (!game.magic_wall_active)
6236 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6237 Store[newx][newy] = Store[x][y];
6239 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6241 Feld[x][y] = get_next_element(element);
6242 if (!game.magic_wall_active)
6243 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6244 element = Feld[newx][newy] = Store[x][y];
6246 else if (element == EL_AMOEBA_DROPPING)
6248 Feld[x][y] = get_next_element(element);
6249 element = Feld[newx][newy] = Store[x][y];
6251 else if (element == EL_SOKOBAN_OBJECT)
6254 Feld[x][y] = Back[x][y];
6256 if (Back[newx][newy])
6257 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6259 Back[x][y] = Back[newx][newy] = 0;
6262 else if (Store[x][y] == EL_ACID)
6264 element = Feld[newx][newy] = EL_ACID;
6268 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6269 ei->move_leave_element != EL_EMPTY &&
6270 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6271 Store[x][y] != EL_EMPTY))
6273 /* some elements can leave other elements behind after moving */
6275 Feld[x][y] = ei->move_leave_element;
6276 InitField(x, y, FALSE);
6278 if (GFX_CRUMBLED(Feld[x][y]))
6279 DrawLevelFieldCrumbledSandNeighbours(x, y);
6283 Store[x][y] = EL_EMPTY;
6284 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6285 MovDelay[newx][newy] = 0;
6287 if (CAN_CHANGE(element))
6289 /* copy element change control values to new field */
6290 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6291 ChangePage[newx][newy] = ChangePage[x][y];
6292 Changed[newx][newy] = Changed[x][y];
6293 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6296 ChangeDelay[x][y] = 0;
6297 ChangePage[x][y] = -1;
6298 Changed[x][y] = CE_BITMASK_DEFAULT;
6299 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6301 /* copy animation control values to new field */
6302 GfxFrame[newx][newy] = GfxFrame[x][y];
6303 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6304 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6305 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6307 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6309 ResetGfxAnimation(x, y); /* reset animation values for old field */
6312 /* some elements can leave other elements behind after moving */
6314 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6315 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6316 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6318 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6319 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6323 int move_leave_element = ei->move_leave_element;
6325 Feld[x][y] = move_leave_element;
6326 InitField(x, y, FALSE);
6328 if (GFX_CRUMBLED(Feld[x][y]))
6329 DrawLevelFieldCrumbledSandNeighbours(x, y);
6331 if (ELEM_IS_PLAYER(move_leave_element))
6332 RelocatePlayer(x, y, move_leave_element);
6337 /* some elements can leave other elements behind after moving */
6338 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6339 ei->move_leave_element != EL_EMPTY &&
6340 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6341 ei->can_leave_element_last))
6343 Feld[x][y] = ei->move_leave_element;
6344 InitField(x, y, FALSE);
6346 if (GFX_CRUMBLED(Feld[x][y]))
6347 DrawLevelFieldCrumbledSandNeighbours(x, y);
6350 ei->can_leave_element_last = ei->can_leave_element;
6351 ei->can_leave_element = FALSE;
6355 /* 2.1.1 (does not work correctly for spring) */
6356 if (!CAN_MOVE(element))
6357 MovDir[newx][newy] = 0;
6361 /* (does not work for falling objects that slide horizontally) */
6362 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6363 MovDir[newx][newy] = 0;
6366 if (!CAN_MOVE(element) ||
6367 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6368 MovDir[newx][newy] = 0;
6372 if (!CAN_MOVE(element) ||
6373 (CAN_FALL(element) && direction == MV_DOWN))
6374 GfxDir[x][y] = MovDir[newx][newy] = 0;
6376 if (!CAN_MOVE(element) ||
6377 (CAN_FALL(element) && direction == MV_DOWN &&
6378 (element == EL_SPRING ||
6379 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6380 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6381 GfxDir[x][y] = MovDir[newx][newy] = 0;
6387 DrawLevelField(x, y);
6388 DrawLevelField(newx, newy);
6390 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6392 /* prevent pushed element from moving on in pushed direction */
6393 if (pushed_by_player && CAN_MOVE(element) &&
6394 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6395 !(element_info[element].move_pattern & direction))
6396 TurnRound(newx, newy);
6399 /* prevent elements on conveyor belt from moving on in last direction */
6400 if (pushed_by_conveyor && CAN_FALL(element) &&
6401 direction & MV_HORIZONTAL)
6404 if (CAN_MOVE(element))
6405 InitMovDir(newx, newy);
6407 MovDir[newx][newy] = 0;
6409 MovDir[newx][newy] = 0;
6414 if (!pushed_by_player)
6416 int nextx = newx + dx, nexty = newy + dy;
6417 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6419 WasJustMoving[newx][newy] = 3;
6421 if (CAN_FALL(element) && direction == MV_DOWN)
6422 WasJustFalling[newx][newy] = 3;
6424 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6425 CheckCollision[newx][newy] = 2;
6428 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6430 TestIfBadThingTouchesHero(newx, newy);
6431 TestIfBadThingTouchesFriend(newx, newy);
6433 if (!IS_CUSTOM_ELEMENT(element))
6434 TestIfBadThingTouchesOtherBadThing(newx, newy);
6436 else if (element == EL_PENGUIN)
6437 TestIfFriendTouchesBadThing(newx, newy);
6439 #if USE_NEW_MOVE_STYLE
6441 if (CAN_FALL(element) && direction == MV_DOWN &&
6442 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6443 IS_PLAYER(x, newy + 1))
6444 printf("::: we would now kill the player [%d]\n", FrameCounter);
6447 /* give the player one last chance (one more frame) to move away */
6448 if (CAN_FALL(element) && direction == MV_DOWN &&
6449 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6450 (!IS_PLAYER(x, newy + 1) ||
6451 game.engine_version < VERSION_IDENT(3,1,1,0)))
6454 if (CAN_FALL(element) && direction == MV_DOWN &&
6455 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6460 if (pushed_by_player)
6463 int dig_side = MV_DIR_OPPOSITE(direction);
6465 static int trigger_sides[4] =
6467 CH_SIDE_RIGHT, /* moving left */
6468 CH_SIDE_LEFT, /* moving right */
6469 CH_SIDE_BOTTOM, /* moving up */
6470 CH_SIDE_TOP, /* moving down */
6472 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6474 struct PlayerInfo *player = PLAYERINFO(x, y);
6476 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6477 player->index_bit, dig_side);
6478 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6479 player->index_bit, dig_side);
6484 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6488 if (ChangePage[newx][newy] != -1) /* delayed change */
6489 ChangeElement(newx, newy, ChangePage[newx][newy]);
6494 TestIfElementHitsCustomElement(newx, newy, direction);
6498 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6500 int hitting_element = Feld[newx][newy];
6502 /* !!! fix side (direction) orientation here and elsewhere !!! */
6503 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6507 if (IN_LEV_FIELD(nextx, nexty))
6509 int opposite_direction = MV_DIR_OPPOSITE(direction);
6510 int hitting_side = direction;
6511 int touched_side = opposite_direction;
6512 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6513 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6514 MovDir[nextx][nexty] != direction ||
6515 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6521 CheckElementChangeBySide(nextx, nexty, touched_element,
6522 CE_HIT_BY_SOMETHING, opposite_direction);
6524 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6525 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6527 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6529 struct ElementChangeInfo *change =
6530 &element_info[hitting_element].change_page[i];
6532 if (change->can_change &&
6533 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6534 change->trigger_side & touched_side &&
6535 change->trigger_element == touched_element)
6537 CheckElementChangeByPage(newx, newy, hitting_element,
6538 touched_element, CE_OTHER_IS_HITTING,i);
6544 if (IS_CUSTOM_ELEMENT(touched_element) &&
6545 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6547 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6549 struct ElementChangeInfo *change =
6550 &element_info[touched_element].change_page[i];
6552 if (change->can_change &&
6553 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6554 change->trigger_side & hitting_side &&
6555 change->trigger_element == hitting_element)
6557 CheckElementChangeByPage(nextx, nexty, touched_element,
6558 hitting_element, CE_OTHER_GETS_HIT, i);
6569 TestIfPlayerTouchesCustomElement(newx, newy);
6570 TestIfElementTouchesCustomElement(newx, newy);
6573 int AmoebeNachbarNr(int ax, int ay)
6576 int element = Feld[ax][ay];
6578 static int xy[4][2] =
6586 for (i = 0; i < NUM_DIRECTIONS; i++)
6588 int x = ax + xy[i][0];
6589 int y = ay + xy[i][1];
6591 if (!IN_LEV_FIELD(x, y))
6594 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6595 group_nr = AmoebaNr[x][y];
6601 void AmoebenVereinigen(int ax, int ay)
6603 int i, x, y, xx, yy;
6604 int new_group_nr = AmoebaNr[ax][ay];
6605 static int xy[4][2] =
6613 if (new_group_nr == 0)
6616 for (i = 0; i < NUM_DIRECTIONS; i++)
6621 if (!IN_LEV_FIELD(x, y))
6624 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6625 Feld[x][y] == EL_BD_AMOEBA ||
6626 Feld[x][y] == EL_AMOEBA_DEAD) &&
6627 AmoebaNr[x][y] != new_group_nr)
6629 int old_group_nr = AmoebaNr[x][y];
6631 if (old_group_nr == 0)
6634 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6635 AmoebaCnt[old_group_nr] = 0;
6636 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6637 AmoebaCnt2[old_group_nr] = 0;
6639 for (yy = 0; yy < lev_fieldy; yy++)
6641 for (xx = 0; xx < lev_fieldx; xx++)
6643 if (AmoebaNr[xx][yy] == old_group_nr)
6644 AmoebaNr[xx][yy] = new_group_nr;
6651 void AmoebeUmwandeln(int ax, int ay)
6655 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6657 int group_nr = AmoebaNr[ax][ay];
6662 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6663 printf("AmoebeUmwandeln(): This should never happen!\n");
6668 for (y = 0; y < lev_fieldy; y++)
6670 for (x = 0; x < lev_fieldx; x++)
6672 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6675 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6679 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6680 SND_AMOEBA_TURNING_TO_GEM :
6681 SND_AMOEBA_TURNING_TO_ROCK));
6686 static int xy[4][2] =
6694 for (i = 0; i < NUM_DIRECTIONS; i++)
6699 if (!IN_LEV_FIELD(x, y))
6702 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6704 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6705 SND_AMOEBA_TURNING_TO_GEM :
6706 SND_AMOEBA_TURNING_TO_ROCK));
6713 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6716 int group_nr = AmoebaNr[ax][ay];
6717 boolean done = FALSE;
6722 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6723 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6728 for (y = 0; y < lev_fieldy; y++)
6730 for (x = 0; x < lev_fieldx; x++)
6732 if (AmoebaNr[x][y] == group_nr &&
6733 (Feld[x][y] == EL_AMOEBA_DEAD ||
6734 Feld[x][y] == EL_BD_AMOEBA ||
6735 Feld[x][y] == EL_AMOEBA_GROWING))
6738 Feld[x][y] = new_element;
6739 InitField(x, y, FALSE);
6740 DrawLevelField(x, y);
6747 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6748 SND_BD_AMOEBA_TURNING_TO_ROCK :
6749 SND_BD_AMOEBA_TURNING_TO_GEM));
6752 void AmoebeWaechst(int x, int y)
6754 static unsigned long sound_delay = 0;
6755 static unsigned long sound_delay_value = 0;
6757 if (!MovDelay[x][y]) /* start new growing cycle */
6761 if (DelayReached(&sound_delay, sound_delay_value))
6764 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6766 if (Store[x][y] == EL_BD_AMOEBA)
6767 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6769 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6771 sound_delay_value = 30;
6775 if (MovDelay[x][y]) /* wait some time before growing bigger */
6778 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6780 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6781 6 - MovDelay[x][y]);
6783 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6786 if (!MovDelay[x][y])
6788 Feld[x][y] = Store[x][y];
6790 DrawLevelField(x, y);
6795 void AmoebaDisappearing(int x, int y)
6797 static unsigned long sound_delay = 0;
6798 static unsigned long sound_delay_value = 0;
6800 if (!MovDelay[x][y]) /* start new shrinking cycle */
6804 if (DelayReached(&sound_delay, sound_delay_value))
6805 sound_delay_value = 30;
6808 if (MovDelay[x][y]) /* wait some time before shrinking */
6811 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6813 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6814 6 - MovDelay[x][y]);
6816 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6819 if (!MovDelay[x][y])
6821 Feld[x][y] = EL_EMPTY;
6822 DrawLevelField(x, y);
6824 /* don't let mole enter this field in this cycle;
6825 (give priority to objects falling to this field from above) */
6831 void AmoebeAbleger(int ax, int ay)
6834 int element = Feld[ax][ay];
6835 int graphic = el2img(element);
6836 int newax = ax, neway = ay;
6837 static int xy[4][2] =
6845 if (!level.amoeba_speed)
6847 Feld[ax][ay] = EL_AMOEBA_DEAD;
6848 DrawLevelField(ax, ay);
6852 if (IS_ANIMATED(graphic))
6853 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6855 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6856 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6858 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6861 if (MovDelay[ax][ay])
6865 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6868 int x = ax + xy[start][0];
6869 int y = ay + xy[start][1];
6871 if (!IN_LEV_FIELD(x, y))
6875 if (IS_FREE(x, y) ||
6876 CAN_GROW_INTO(Feld[x][y]) ||
6877 Feld[x][y] == EL_QUICKSAND_EMPTY)
6883 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6884 if (IS_FREE(x, y) ||
6885 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6892 if (newax == ax && neway == ay)
6895 else /* normal or "filled" (BD style) amoeba */
6898 boolean waiting_for_player = FALSE;
6900 for (i = 0; i < NUM_DIRECTIONS; i++)
6902 int j = (start + i) % 4;
6903 int x = ax + xy[j][0];
6904 int y = ay + xy[j][1];
6906 if (!IN_LEV_FIELD(x, y))
6910 if (IS_FREE(x, y) ||
6911 CAN_GROW_INTO(Feld[x][y]) ||
6912 Feld[x][y] == EL_QUICKSAND_EMPTY)
6919 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6920 if (IS_FREE(x, y) ||
6921 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6928 else if (IS_PLAYER(x, y))
6929 waiting_for_player = TRUE;
6932 if (newax == ax && neway == ay) /* amoeba cannot grow */
6935 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6937 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6940 Feld[ax][ay] = EL_AMOEBA_DEAD;
6941 DrawLevelField(ax, ay);
6942 AmoebaCnt[AmoebaNr[ax][ay]]--;
6944 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6946 if (element == EL_AMOEBA_FULL)
6947 AmoebeUmwandeln(ax, ay);
6948 else if (element == EL_BD_AMOEBA)
6949 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6954 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6956 /* amoeba gets larger by growing in some direction */
6958 int new_group_nr = AmoebaNr[ax][ay];
6961 if (new_group_nr == 0)
6963 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6964 printf("AmoebeAbleger(): This should never happen!\n");
6969 AmoebaNr[newax][neway] = new_group_nr;
6970 AmoebaCnt[new_group_nr]++;
6971 AmoebaCnt2[new_group_nr]++;
6973 /* if amoeba touches other amoeba(s) after growing, unify them */
6974 AmoebenVereinigen(newax, neway);
6976 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6978 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6984 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6985 (neway == lev_fieldy - 1 && newax != ax))
6987 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6988 Store[newax][neway] = element;
6990 else if (neway == ay)
6992 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6994 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6996 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7001 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7002 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7003 Store[ax][ay] = EL_AMOEBA_DROP;
7004 ContinueMoving(ax, ay);
7008 DrawLevelField(newax, neway);
7011 void Life(int ax, int ay)
7014 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7016 int element = Feld[ax][ay];
7017 int graphic = el2img(element);
7018 boolean changed = FALSE;
7020 if (IS_ANIMATED(graphic))
7021 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7026 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7027 MovDelay[ax][ay] = life_time;
7029 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7032 if (MovDelay[ax][ay])
7036 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7038 int xx = ax+x1, yy = ay+y1;
7041 if (!IN_LEV_FIELD(xx, yy))
7044 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7046 int x = xx+x2, y = yy+y2;
7048 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7051 if (((Feld[x][y] == element ||
7052 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7054 (IS_FREE(x, y) && Stop[x][y]))
7058 if (xx == ax && yy == ay) /* field in the middle */
7060 if (nachbarn < life[0] || nachbarn > life[1])
7062 Feld[xx][yy] = EL_EMPTY;
7064 DrawLevelField(xx, yy);
7065 Stop[xx][yy] = TRUE;
7070 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7071 { /* free border field */
7072 if (nachbarn >= life[2] && nachbarn <= life[3])
7074 Feld[xx][yy] = element;
7075 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7077 DrawLevelField(xx, yy);
7078 Stop[xx][yy] = TRUE;
7083 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7084 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7085 { /* free border field */
7086 if (nachbarn >= life[2] && nachbarn <= life[3])
7088 Feld[xx][yy] = element;
7089 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7091 DrawLevelField(xx, yy);
7092 Stop[xx][yy] = TRUE;
7100 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7101 SND_GAME_OF_LIFE_GROWING);
7104 static void InitRobotWheel(int x, int y)
7106 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7109 static void RunRobotWheel(int x, int y)
7111 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7114 static void StopRobotWheel(int x, int y)
7116 if (ZX == x && ZY == y)
7120 static void InitTimegateWheel(int x, int y)
7123 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7125 /* another brainless, "type style" bug ... :-( */
7126 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7130 static void RunTimegateWheel(int x, int y)
7132 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7135 void CheckExit(int x, int y)
7137 if (local_player->gems_still_needed > 0 ||
7138 local_player->sokobanfields_still_needed > 0 ||
7139 local_player->lights_still_needed > 0)
7141 int element = Feld[x][y];
7142 int graphic = el2img(element);
7144 if (IS_ANIMATED(graphic))
7145 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7150 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7153 Feld[x][y] = EL_EXIT_OPENING;
7155 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7158 void CheckExitSP(int x, int y)
7160 if (local_player->gems_still_needed > 0)
7162 int element = Feld[x][y];
7163 int graphic = el2img(element);
7165 if (IS_ANIMATED(graphic))
7166 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7171 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7174 Feld[x][y] = EL_SP_EXIT_OPENING;
7176 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7179 static void CloseAllOpenTimegates()
7183 for (y = 0; y < lev_fieldy; y++)
7185 for (x = 0; x < lev_fieldx; x++)
7187 int element = Feld[x][y];
7189 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7191 Feld[x][y] = EL_TIMEGATE_CLOSING;
7193 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7195 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7202 void EdelsteinFunkeln(int x, int y)
7204 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7207 if (Feld[x][y] == EL_BD_DIAMOND)
7210 if (MovDelay[x][y] == 0) /* next animation frame */
7211 MovDelay[x][y] = 11 * !SimpleRND(500);
7213 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7217 if (setup.direct_draw && MovDelay[x][y])
7218 SetDrawtoField(DRAW_BUFFERED);
7220 DrawLevelElementAnimation(x, y, Feld[x][y]);
7222 if (MovDelay[x][y] != 0)
7224 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7225 10 - MovDelay[x][y]);
7227 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7229 if (setup.direct_draw)
7233 dest_x = FX + SCREENX(x) * TILEX;
7234 dest_y = FY + SCREENY(y) * TILEY;
7236 BlitBitmap(drawto_field, window,
7237 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7238 SetDrawtoField(DRAW_DIRECT);
7244 void MauerWaechst(int x, int y)
7248 if (!MovDelay[x][y]) /* next animation frame */
7249 MovDelay[x][y] = 3 * delay;
7251 if (MovDelay[x][y]) /* wait some time before next frame */
7255 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7257 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7258 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7260 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7263 if (!MovDelay[x][y])
7265 if (MovDir[x][y] == MV_LEFT)
7267 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7268 DrawLevelField(x - 1, y);
7270 else if (MovDir[x][y] == MV_RIGHT)
7272 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7273 DrawLevelField(x + 1, y);
7275 else if (MovDir[x][y] == MV_UP)
7277 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7278 DrawLevelField(x, y - 1);
7282 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7283 DrawLevelField(x, y + 1);
7286 Feld[x][y] = Store[x][y];
7288 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7289 DrawLevelField(x, y);
7294 void MauerAbleger(int ax, int ay)
7296 int element = Feld[ax][ay];
7297 int graphic = el2img(element);
7298 boolean oben_frei = FALSE, unten_frei = FALSE;
7299 boolean links_frei = FALSE, rechts_frei = FALSE;
7300 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7301 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7302 boolean new_wall = FALSE;
7304 if (IS_ANIMATED(graphic))
7305 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7307 if (!MovDelay[ax][ay]) /* start building new wall */
7308 MovDelay[ax][ay] = 6;
7310 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7313 if (MovDelay[ax][ay])
7317 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7319 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7321 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7323 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7326 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7327 element == EL_EXPANDABLE_WALL_ANY)
7331 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7332 Store[ax][ay-1] = element;
7333 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7334 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7335 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7336 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7341 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7342 Store[ax][ay+1] = element;
7343 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7344 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7345 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7346 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7351 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7352 element == EL_EXPANDABLE_WALL_ANY ||
7353 element == EL_EXPANDABLE_WALL)
7357 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7358 Store[ax-1][ay] = element;
7359 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7360 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7361 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7362 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7368 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7369 Store[ax+1][ay] = element;
7370 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7371 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7372 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7373 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7378 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7379 DrawLevelField(ax, ay);
7381 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7383 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7384 unten_massiv = TRUE;
7385 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7386 links_massiv = TRUE;
7387 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7388 rechts_massiv = TRUE;
7390 if (((oben_massiv && unten_massiv) ||
7391 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7392 element == EL_EXPANDABLE_WALL) &&
7393 ((links_massiv && rechts_massiv) ||
7394 element == EL_EXPANDABLE_WALL_VERTICAL))
7395 Feld[ax][ay] = EL_WALL;
7399 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7401 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7405 void CheckForDragon(int x, int y)
7408 boolean dragon_found = FALSE;
7409 static int xy[4][2] =
7417 for (i = 0; i < NUM_DIRECTIONS; i++)
7419 for (j = 0; j < 4; j++)
7421 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7423 if (IN_LEV_FIELD(xx, yy) &&
7424 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7426 if (Feld[xx][yy] == EL_DRAGON)
7427 dragon_found = TRUE;
7436 for (i = 0; i < NUM_DIRECTIONS; i++)
7438 for (j = 0; j < 3; j++)
7440 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7442 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7444 Feld[xx][yy] = EL_EMPTY;
7445 DrawLevelField(xx, yy);
7454 static void InitBuggyBase(int x, int y)
7456 int element = Feld[x][y];
7457 int activating_delay = FRAMES_PER_SECOND / 4;
7460 (element == EL_SP_BUGGY_BASE ?
7461 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7462 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7464 element == EL_SP_BUGGY_BASE_ACTIVE ?
7465 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7468 static void WarnBuggyBase(int x, int y)
7471 static int xy[4][2] =
7479 for (i = 0; i < NUM_DIRECTIONS; i++)
7481 int xx = x + xy[i][0], yy = y + xy[i][1];
7483 if (IS_PLAYER(xx, yy))
7485 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7492 static void InitTrap(int x, int y)
7494 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7497 static void ActivateTrap(int x, int y)
7499 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7502 static void ChangeActiveTrap(int x, int y)
7504 int graphic = IMG_TRAP_ACTIVE;
7506 /* if new animation frame was drawn, correct crumbled sand border */
7507 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7508 DrawLevelFieldCrumbledSand(x, y);
7511 static void ChangeElementNowExt(int x, int y, int target_element)
7513 int previous_move_direction = MovDir[x][y];
7515 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7516 IS_WALKABLE(Feld[x][y]));
7518 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7519 IS_WALKABLE(Feld[x][y]) &&
7523 /* check if element under player changes from accessible to unaccessible
7524 (needed for special case of dropping element which then changes) */
7525 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7526 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7529 printf("::: BOOOM! [%d, '%s']\n", target_element,
7530 element_info[target_element].token_name);
7542 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7543 RemoveMovingField(x, y);
7547 Feld[x][y] = target_element;
7550 Feld[x][y] = target_element;
7553 ResetGfxAnimation(x, y);
7554 ResetRandomAnimationValue(x, y);
7556 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7557 MovDir[x][y] = previous_move_direction;
7560 InitField_WithBug1(x, y, FALSE);
7562 InitField(x, y, FALSE);
7563 if (CAN_MOVE(Feld[x][y]))
7567 DrawLevelField(x, y);
7569 if (GFX_CRUMBLED(Feld[x][y]))
7570 DrawLevelFieldCrumbledSandNeighbours(x, y);
7574 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7578 TestIfBadThingTouchesHero(x, y);
7579 TestIfPlayerTouchesCustomElement(x, y);
7580 TestIfElementTouchesCustomElement(x, y);
7583 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7584 if (ELEM_IS_PLAYER(target_element))
7585 RelocatePlayer(x, y, target_element);
7588 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7592 TestIfBadThingTouchesHero(x, y);
7593 TestIfPlayerTouchesCustomElement(x, y);
7594 TestIfElementTouchesCustomElement(x, y);
7598 static boolean ChangeElementNow(int x, int y, int element, int page)
7600 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7602 int old_element = Feld[x][y];
7604 /* always use default change event to prevent running into a loop */
7605 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7606 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7608 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7610 /* reset actual trigger element and player */
7611 change->actual_trigger_element = EL_EMPTY;
7612 change->actual_trigger_player = EL_PLAYER_1;
7615 /* do not change already changed elements with same change event */
7617 if (Changed[x][y] & ChangeEvent[x][y])
7624 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7627 /* !!! indirect change before direct change !!! */
7628 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7631 if (change->explode)
7638 if (change->use_target_content)
7640 boolean complete_replace = TRUE;
7641 boolean can_replace[3][3];
7644 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7647 boolean is_walkable;
7648 boolean is_diggable;
7649 boolean is_collectible;
7650 boolean is_removable;
7651 boolean is_destructible;
7652 int ex = x + xx - 1;
7653 int ey = y + yy - 1;
7654 int content_element = change->target_content[xx][yy];
7657 can_replace[xx][yy] = TRUE;
7659 if (ex == x && ey == y) /* do not check changing element itself */
7662 if (content_element == EL_EMPTY_SPACE)
7664 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7669 if (!IN_LEV_FIELD(ex, ey))
7671 can_replace[xx][yy] = FALSE;
7672 complete_replace = FALSE;
7678 if (Changed[ex][ey]) /* do not change already changed elements */
7680 can_replace[xx][yy] = FALSE;
7681 complete_replace = FALSE;
7689 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7690 e = MovingOrBlocked2Element(ex, ey);
7695 is_empty = (IS_FREE(ex, ey) ||
7696 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7697 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7698 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7702 is_empty = (IS_FREE(ex, ey) ||
7703 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7705 is_empty = (IS_FREE(ex, ey) ||
7706 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7711 is_walkable = (is_empty || IS_WALKABLE(e));
7712 is_diggable = (is_empty || IS_DIGGABLE(e));
7713 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7714 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7715 is_removable = (is_diggable || is_collectible);
7717 can_replace[xx][yy] =
7718 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7719 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7720 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7721 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7722 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7723 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7724 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7726 if (!can_replace[xx][yy])
7727 complete_replace = FALSE;
7729 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7730 IS_WALKABLE(content_element)));
7732 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7734 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7737 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7738 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7739 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7741 can_replace[xx][yy] = FALSE;
7742 complete_replace = FALSE;
7747 if (!change->only_if_complete || complete_replace)
7749 boolean something_has_changed = FALSE;
7751 if (change->only_if_complete && change->use_random_replace &&
7752 RND(100) < change->random_percentage)
7755 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7757 int ex = x + xx - 1;
7758 int ey = y + yy - 1;
7759 int content_element;
7761 if (can_replace[xx][yy] && (!change->use_random_replace ||
7762 RND(100) < change->random_percentage))
7764 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7765 RemoveMovingField(ex, ey);
7767 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7769 content_element = change->target_content[xx][yy];
7770 target_element = GET_TARGET_ELEMENT(content_element, change);
7772 ChangeElementNowExt(ex, ey, target_element);
7774 something_has_changed = TRUE;
7776 /* for symmetry reasons, freeze newly created border elements */
7777 if (ex != x || ey != y)
7778 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7782 if (something_has_changed)
7783 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7788 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7790 ChangeElementNowExt(x, y, target_element);
7792 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7796 /* this uses direct change before indirect change */
7797 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7803 static void ChangeElement(int x, int y, int page)
7805 int element = MovingOrBlocked2Element(x, y);
7806 struct ElementInfo *ei = &element_info[element];
7807 struct ElementChangeInfo *change = &ei->change_page[page];
7810 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7813 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7814 x, y, element, element_info[element].token_name);
7815 printf("ChangeElement(): This should never happen!\n");
7820 /* this can happen with classic bombs on walkable, changing elements */
7821 if (!CAN_CHANGE(element))
7824 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7825 ChangeDelay[x][y] = 0;
7831 if (ChangeDelay[x][y] == 0) /* initialize element change */
7833 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7834 RND(change->delay_random * change->delay_frames)) + 1;
7836 ResetGfxAnimation(x, y);
7837 ResetRandomAnimationValue(x, y);
7839 if (change->pre_change_function)
7840 change->pre_change_function(x, y);
7843 ChangeDelay[x][y]--;
7845 if (ChangeDelay[x][y] != 0) /* continue element change */
7847 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7849 if (IS_ANIMATED(graphic))
7850 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7852 if (change->change_function)
7853 change->change_function(x, y);
7855 else /* finish element change */
7857 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7859 page = ChangePage[x][y];
7860 ChangePage[x][y] = -1;
7862 change = &ei->change_page[page];
7866 if (IS_MOVING(x, y) && !change->explode)
7868 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7871 ChangeDelay[x][y] = 1; /* try change after next move step */
7872 ChangePage[x][y] = page; /* remember page to use for change */
7877 if (ChangeElementNow(x, y, element, page))
7879 if (change->post_change_function)
7880 change->post_change_function(x, y);
7885 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7886 int trigger_element,
7893 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7895 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7898 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7900 int element = EL_CUSTOM_START + i;
7902 boolean change_element = FALSE;
7905 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7908 for (j = 0; j < element_info[element].num_change_pages; j++)
7910 struct ElementChangeInfo *change = &element_info[element].change_page[j];
7912 if (change->can_change &&
7913 change->events & CH_EVENT_BIT(trigger_event) &&
7914 change->trigger_side & trigger_side &&
7915 change->trigger_player & trigger_player &&
7916 change->trigger_page & trigger_page_bits &&
7917 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7920 if (!(change->events & CH_EVENT_BIT(trigger_event)))
7921 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7922 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7925 change_element = TRUE;
7928 change->actual_trigger_element = trigger_element;
7929 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7935 if (!change_element)
7938 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7941 if (x == lx && y == ly) /* do not change trigger element itself */
7945 if (Feld[x][y] == element)
7947 ChangeDelay[x][y] = 1;
7948 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7949 ChangeElement(x, y, page);
7957 static boolean CheckElementChangeExt(int x, int y,
7959 int trigger_element,
7965 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7968 if (Feld[x][y] == EL_BLOCKED)
7970 Blocked2Moving(x, y, &x, &y);
7971 element = Feld[x][y];
7975 if (Feld[x][y] != element) /* check if element has already changed */
7978 printf("::: %d ('%s') != %d ('%s') [%d]\n",
7979 Feld[x][y], element_info[Feld[x][y]].token_name,
7980 element, element_info[element].token_name,
7989 if (trigger_page < 0)
7991 boolean change_element = FALSE;
7994 for (i = 0; i < element_info[element].num_change_pages; i++)
7996 struct ElementChangeInfo *change = &element_info[element].change_page[i];
7998 if (change->can_change &&
7999 change->events & CH_EVENT_BIT(trigger_event) &&
8000 change->trigger_side & trigger_side &&
8001 change->trigger_player & trigger_player)
8003 change_element = TRUE;
8006 change->actual_trigger_element = trigger_element;
8007 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8013 if (!change_element)
8018 struct ElementInfo *ei = &element_info[element];
8019 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8021 change->actual_trigger_element = trigger_element;
8022 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8027 /* !!! this check misses pages with same event, but different side !!! */
8029 if (trigger_page < 0)
8030 trigger_page = element_info[element].event_page_nr[trigger_event];
8032 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8036 ChangeDelay[x][y] = 1;
8037 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8038 ChangeElement(x, y, trigger_page);
8043 static void PlayPlayerSound(struct PlayerInfo *player)
8045 int jx = player->jx, jy = player->jy;
8046 int element = player->element_nr;
8047 int last_action = player->last_action_waiting;
8048 int action = player->action_waiting;
8050 if (player->is_waiting)
8052 if (action != last_action)
8053 PlayLevelSoundElementAction(jx, jy, element, action);
8055 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8059 if (action != last_action)
8060 StopSound(element_info[element].sound[last_action]);
8062 if (last_action == ACTION_SLEEPING)
8063 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8067 static void PlayAllPlayersSound()
8071 for (i = 0; i < MAX_PLAYERS; i++)
8072 if (stored_player[i].active)
8073 PlayPlayerSound(&stored_player[i]);
8076 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8078 boolean last_waiting = player->is_waiting;
8079 int move_dir = player->MovDir;
8081 player->last_action_waiting = player->action_waiting;
8085 if (!last_waiting) /* not waiting -> waiting */
8087 player->is_waiting = TRUE;
8089 player->frame_counter_bored =
8091 game.player_boring_delay_fixed +
8092 SimpleRND(game.player_boring_delay_random);
8093 player->frame_counter_sleeping =
8095 game.player_sleeping_delay_fixed +
8096 SimpleRND(game.player_sleeping_delay_random);
8098 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8101 if (game.player_sleeping_delay_fixed +
8102 game.player_sleeping_delay_random > 0 &&
8103 player->anim_delay_counter == 0 &&
8104 player->post_delay_counter == 0 &&
8105 FrameCounter >= player->frame_counter_sleeping)
8106 player->is_sleeping = TRUE;
8107 else if (game.player_boring_delay_fixed +
8108 game.player_boring_delay_random > 0 &&
8109 FrameCounter >= player->frame_counter_bored)
8110 player->is_bored = TRUE;
8112 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8113 player->is_bored ? ACTION_BORING :
8116 if (player->is_sleeping)
8118 if (player->num_special_action_sleeping > 0)
8120 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8122 int last_special_action = player->special_action_sleeping;
8123 int num_special_action = player->num_special_action_sleeping;
8124 int special_action =
8125 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8126 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8127 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8128 last_special_action + 1 : ACTION_SLEEPING);
8129 int special_graphic =
8130 el_act_dir2img(player->element_nr, special_action, move_dir);
8132 player->anim_delay_counter =
8133 graphic_info[special_graphic].anim_delay_fixed +
8134 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8135 player->post_delay_counter =
8136 graphic_info[special_graphic].post_delay_fixed +
8137 SimpleRND(graphic_info[special_graphic].post_delay_random);
8139 player->special_action_sleeping = special_action;
8142 if (player->anim_delay_counter > 0)
8144 player->action_waiting = player->special_action_sleeping;
8145 player->anim_delay_counter--;
8147 else if (player->post_delay_counter > 0)
8149 player->post_delay_counter--;
8153 else if (player->is_bored)
8155 if (player->num_special_action_bored > 0)
8157 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8159 int special_action =
8160 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8161 int special_graphic =
8162 el_act_dir2img(player->element_nr, special_action, move_dir);
8164 player->anim_delay_counter =
8165 graphic_info[special_graphic].anim_delay_fixed +
8166 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8167 player->post_delay_counter =
8168 graphic_info[special_graphic].post_delay_fixed +
8169 SimpleRND(graphic_info[special_graphic].post_delay_random);
8171 player->special_action_bored = special_action;
8174 if (player->anim_delay_counter > 0)
8176 player->action_waiting = player->special_action_bored;
8177 player->anim_delay_counter--;
8179 else if (player->post_delay_counter > 0)
8181 player->post_delay_counter--;
8186 else if (last_waiting) /* waiting -> not waiting */
8188 player->is_waiting = FALSE;
8189 player->is_bored = FALSE;
8190 player->is_sleeping = FALSE;
8192 player->frame_counter_bored = -1;
8193 player->frame_counter_sleeping = -1;
8195 player->anim_delay_counter = 0;
8196 player->post_delay_counter = 0;
8198 player->action_waiting = ACTION_DEFAULT;
8200 player->special_action_bored = ACTION_DEFAULT;
8201 player->special_action_sleeping = ACTION_DEFAULT;
8206 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8209 static byte stored_player_action[MAX_PLAYERS];
8210 static int num_stored_actions = 0;
8212 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8213 int left = player_action & JOY_LEFT;
8214 int right = player_action & JOY_RIGHT;
8215 int up = player_action & JOY_UP;
8216 int down = player_action & JOY_DOWN;
8217 int button1 = player_action & JOY_BUTTON_1;
8218 int button2 = player_action & JOY_BUTTON_2;
8219 int dx = (left ? -1 : right ? 1 : 0);
8220 int dy = (up ? -1 : down ? 1 : 0);
8223 stored_player_action[player->index_nr] = 0;
8224 num_stored_actions++;
8228 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8231 if (!player->active || tape.pausing)
8235 printf("::: [%d %d %d %d] [%d %d]\n",
8236 left, right, up, down, button1, button2);
8242 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8247 if (player->MovPos == 0)
8248 CheckGravityMovement(player);
8251 snapped = SnapField(player, dx, dy);
8255 dropped = DropElement(player);
8257 moved = MovePlayer(player, dx, dy);
8260 if (tape.single_step && tape.recording && !tape.pausing)
8262 if (button1 || (dropped && !moved))
8264 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8265 SnapField(player, 0, 0); /* stop snapping */
8269 SetPlayerWaiting(player, FALSE);
8272 return player_action;
8274 stored_player_action[player->index_nr] = player_action;
8280 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8283 /* no actions for this player (no input at player's configured device) */
8285 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8286 SnapField(player, 0, 0);
8287 CheckGravityMovementWhenNotMoving(player);
8289 if (player->MovPos == 0)
8290 SetPlayerWaiting(player, TRUE);
8292 if (player->MovPos == 0) /* needed for tape.playing */
8293 player->is_moving = FALSE;
8295 player->is_dropping = FALSE;
8301 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8303 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8305 TapeRecordAction(stored_player_action);
8306 num_stored_actions = 0;
8313 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8315 static byte stored_player_action[MAX_PLAYERS];
8316 static int num_stored_actions = 0;
8317 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8318 int left = player_action & JOY_LEFT;
8319 int right = player_action & JOY_RIGHT;
8320 int up = player_action & JOY_UP;
8321 int down = player_action & JOY_DOWN;
8322 int button1 = player_action & JOY_BUTTON_1;
8323 int button2 = player_action & JOY_BUTTON_2;
8324 int dx = (left ? -1 : right ? 1 : 0);
8325 int dy = (up ? -1 : down ? 1 : 0);
8327 stored_player_action[player->index_nr] = 0;
8328 num_stored_actions++;
8330 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8332 if (!player->active || tape.pausing)
8337 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8340 snapped = SnapField(player, dx, dy);
8344 dropped = DropElement(player);
8346 moved = MovePlayer(player, dx, dy);
8349 if (tape.single_step && tape.recording && !tape.pausing)
8351 if (button1 || (dropped && !moved))
8353 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8354 SnapField(player, 0, 0); /* stop snapping */
8358 stored_player_action[player->index_nr] = player_action;
8362 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8364 /* no actions for this player (no input at player's configured device) */
8366 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8367 SnapField(player, 0, 0);
8368 CheckGravityMovementWhenNotMoving(player);
8370 if (player->MovPos == 0)
8371 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8373 if (player->MovPos == 0) /* needed for tape.playing */
8374 player->is_moving = FALSE;
8377 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8379 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8381 TapeRecordAction(stored_player_action);
8382 num_stored_actions = 0;
8387 void AdvanceFrameAndPlayerCounters(int player_nr)
8391 /* advance frame counters (global frame counter and time frame counter) */
8395 /* advance player counters (counters for move delay, move animation etc.) */
8396 for (i = 0; i < MAX_PLAYERS; i++)
8398 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8400 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8402 if (!advance_player_counters) /* not all players may be affected */
8405 stored_player[i].Frame += move_frames;
8407 if (stored_player[i].MovPos != 0)
8408 stored_player[i].StepFrame += move_frames;
8410 #if USE_NEW_MOVE_DELAY
8411 if (stored_player[i].move_delay > 0)
8412 stored_player[i].move_delay--;
8415 #if USE_NEW_PUSH_DELAY
8416 /* due to bugs in previous versions, counter must count up, not down */
8417 if (stored_player[i].push_delay != -1)
8418 stored_player[i].push_delay++;
8421 if (stored_player[i].drop_delay > 0)
8422 stored_player[i].drop_delay--;
8428 static unsigned long action_delay = 0;
8429 unsigned long action_delay_value;
8430 int magic_wall_x = 0, magic_wall_y = 0;
8431 int i, x, y, element, graphic;
8432 byte *recorded_player_action;
8433 byte summarized_player_action = 0;
8435 byte tape_action[MAX_PLAYERS];
8438 if (game_status != GAME_MODE_PLAYING)
8441 action_delay_value =
8442 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8444 if (tape.playing && tape.warp_forward && !tape.pausing)
8445 action_delay_value = 0;
8447 /* ---------- main game synchronization point ---------- */
8449 WaitUntilDelayReached(&action_delay, action_delay_value);
8451 if (network_playing && !network_player_action_received)
8455 printf("DEBUG: try to get network player actions in time\n");
8459 #if defined(NETWORK_AVALIABLE)
8460 /* last chance to get network player actions without main loop delay */
8464 if (game_status != GAME_MODE_PLAYING)
8467 if (!network_player_action_received)
8471 printf("DEBUG: failed to get network player actions in time\n");
8482 printf("::: getting new tape action [%d]\n", FrameCounter);
8485 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8488 if (recorded_player_action == NULL && tape.pausing)
8493 printf("::: %d\n", stored_player[0].action);
8497 if (recorded_player_action != NULL)
8498 for (i = 0; i < MAX_PLAYERS; i++)
8499 stored_player[i].action = recorded_player_action[i];
8502 for (i = 0; i < MAX_PLAYERS; i++)
8504 summarized_player_action |= stored_player[i].action;
8506 if (!network_playing)
8507 stored_player[i].effective_action = stored_player[i].action;
8510 #if defined(NETWORK_AVALIABLE)
8511 if (network_playing)
8512 SendToServer_MovePlayer(summarized_player_action);
8515 if (!options.network && !setup.team_mode)
8516 local_player->effective_action = summarized_player_action;
8519 if (recorded_player_action != NULL)
8520 for (i = 0; i < MAX_PLAYERS; i++)
8521 stored_player[i].effective_action = recorded_player_action[i];
8525 for (i = 0; i < MAX_PLAYERS; i++)
8527 tape_action[i] = stored_player[i].effective_action;
8529 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8530 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8533 /* only save actions from input devices, but not programmed actions */
8535 TapeRecordAction(tape_action);
8538 for (i = 0; i < MAX_PLAYERS; i++)
8540 int actual_player_action = stored_player[i].effective_action;
8543 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8544 - rnd_equinox_tetrachloride 048
8545 - rnd_equinox_tetrachloride_ii 096
8546 - rnd_emanuel_schmieg 002
8547 - doctor_sloan_ww 001, 020
8549 if (stored_player[i].MovPos == 0)
8550 CheckGravityMovement(&stored_player[i]);
8554 /* overwrite programmed action with tape action */
8555 if (stored_player[i].programmed_action)
8556 actual_player_action = stored_player[i].programmed_action;
8560 if (stored_player[i].programmed_action)
8561 printf("::: %d\n", stored_player[i].programmed_action);
8564 if (recorded_player_action)
8567 if (stored_player[i].programmed_action &&
8568 stored_player[i].programmed_action != recorded_player_action[i])
8569 printf("::: %d: %d <-> %d\n", i,
8570 stored_player[i].programmed_action, recorded_player_action[i]);
8574 actual_player_action = recorded_player_action[i];
8579 /* overwrite tape action with programmed action */
8580 if (stored_player[i].programmed_action)
8581 actual_player_action = stored_player[i].programmed_action;
8586 printf("::: action: %d: %x [%d]\n",
8587 stored_player[i].MovPos, actual_player_action, FrameCounter);
8591 PlayerActions(&stored_player[i], actual_player_action);
8593 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8595 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8596 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8599 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8604 TapeRecordAction(tape_action);
8607 network_player_action_received = FALSE;
8609 ScrollScreen(NULL, SCROLL_GO_ON);
8615 for (i = 0; i < MAX_PLAYERS; i++)
8616 stored_player[i].Frame++;
8620 /* for downwards compatibility, the following code emulates a fixed bug that
8621 occured when pushing elements (causing elements that just made their last
8622 pushing step to already (if possible) make their first falling step in the
8623 same game frame, which is bad); this code is also needed to use the famous
8624 "spring push bug" which is used in older levels and might be wanted to be
8625 used also in newer levels, but in this case the buggy pushing code is only
8626 affecting the "spring" element and no other elements */
8629 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8631 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8634 for (i = 0; i < MAX_PLAYERS; i++)
8636 struct PlayerInfo *player = &stored_player[i];
8641 if (player->active && player->is_pushing && player->is_moving &&
8643 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8644 Feld[x][y] == EL_SPRING))
8646 if (player->active && player->is_pushing && player->is_moving &&
8650 ContinueMoving(x, y);
8652 /* continue moving after pushing (this is actually a bug) */
8653 if (!IS_MOVING(x, y))
8662 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8664 Changed[x][y] = CE_BITMASK_DEFAULT;
8665 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8667 #if USE_NEW_BLOCK_STYLE
8668 /* this must be handled before main playfield loop */
8669 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8672 if (MovDelay[x][y] <= 0)
8678 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8680 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8681 printf("GameActions(): This should never happen!\n");
8683 ChangePage[x][y] = -1;
8688 if (WasJustMoving[x][y] > 0)
8689 WasJustMoving[x][y]--;
8690 if (WasJustFalling[x][y] > 0)
8691 WasJustFalling[x][y]--;
8692 if (CheckCollision[x][y] > 0)
8693 CheckCollision[x][y]--;
8698 /* reset finished pushing action (not done in ContinueMoving() to allow
8699 continous pushing animation for elements with zero push delay) */
8700 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8702 ResetGfxAnimation(x, y);
8703 DrawLevelField(x, y);
8708 if (IS_BLOCKED(x, y))
8712 Blocked2Moving(x, y, &oldx, &oldy);
8713 if (!IS_MOVING(oldx, oldy))
8715 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8716 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8717 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8718 printf("GameActions(): This should never happen!\n");
8724 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8726 element = Feld[x][y];
8728 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8730 graphic = el2img(element);
8736 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8738 element = graphic = 0;
8742 if (graphic_info[graphic].anim_global_sync)
8743 GfxFrame[x][y] = FrameCounter;
8745 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8746 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8747 ResetRandomAnimationValue(x, y);
8749 SetRandomAnimationValue(x, y);
8752 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8755 if (IS_INACTIVE(element))
8757 if (IS_ANIMATED(graphic))
8758 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8764 /* this may take place after moving, so 'element' may have changed */
8766 if (IS_CHANGING(x, y))
8768 if (IS_CHANGING(x, y) &&
8769 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8773 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8774 element_info[element].event_page_nr[CE_DELAY]);
8776 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8779 element = Feld[x][y];
8780 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8784 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8789 element = Feld[x][y];
8790 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8792 if (element == EL_MOLE)
8793 printf("::: %d, %d, %d [%d]\n",
8794 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8798 if (element == EL_YAMYAM)
8799 printf("::: %d, %d, %d\n",
8800 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8804 if (IS_ANIMATED(graphic) &&
8808 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8811 if (element == EL_BUG)
8812 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8816 if (element == EL_MOLE)
8817 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8821 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8822 EdelsteinFunkeln(x, y);
8824 else if ((element == EL_ACID ||
8825 element == EL_EXIT_OPEN ||
8826 element == EL_SP_EXIT_OPEN ||
8827 element == EL_SP_TERMINAL ||
8828 element == EL_SP_TERMINAL_ACTIVE ||
8829 element == EL_EXTRA_TIME ||
8830 element == EL_SHIELD_NORMAL ||
8831 element == EL_SHIELD_DEADLY) &&
8832 IS_ANIMATED(graphic))
8833 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8834 else if (IS_MOVING(x, y))
8835 ContinueMoving(x, y);
8836 else if (IS_ACTIVE_BOMB(element))
8837 CheckDynamite(x, y);
8839 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8840 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8842 else if (element == EL_AMOEBA_GROWING)
8843 AmoebeWaechst(x, y);
8844 else if (element == EL_AMOEBA_SHRINKING)
8845 AmoebaDisappearing(x, y);
8847 #if !USE_NEW_AMOEBA_CODE
8848 else if (IS_AMOEBALIVE(element))
8849 AmoebeAbleger(x, y);
8852 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8854 else if (element == EL_EXIT_CLOSED)
8856 else if (element == EL_SP_EXIT_CLOSED)
8858 else if (element == EL_EXPANDABLE_WALL_GROWING)
8860 else if (element == EL_EXPANDABLE_WALL ||
8861 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8862 element == EL_EXPANDABLE_WALL_VERTICAL ||
8863 element == EL_EXPANDABLE_WALL_ANY)
8865 else if (element == EL_FLAMES)
8866 CheckForDragon(x, y);
8868 else if (IS_AUTO_CHANGING(element))
8869 ChangeElement(x, y);
8871 else if (element == EL_EXPLOSION)
8872 ; /* drawing of correct explosion animation is handled separately */
8873 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8877 /* this may take place after moving, so 'element' may have changed */
8878 if (IS_AUTO_CHANGING(Feld[x][y]))
8879 ChangeElement(x, y);
8882 if (IS_BELT_ACTIVE(element))
8883 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8885 if (game.magic_wall_active)
8887 int jx = local_player->jx, jy = local_player->jy;
8889 /* play the element sound at the position nearest to the player */
8890 if ((element == EL_MAGIC_WALL_FULL ||
8891 element == EL_MAGIC_WALL_ACTIVE ||
8892 element == EL_MAGIC_WALL_EMPTYING ||
8893 element == EL_BD_MAGIC_WALL_FULL ||
8894 element == EL_BD_MAGIC_WALL_ACTIVE ||
8895 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8896 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8904 #if USE_NEW_AMOEBA_CODE
8905 /* new experimental amoeba growth stuff */
8907 if (!(FrameCounter % 8))
8910 static unsigned long random = 1684108901;
8912 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8915 x = (random >> 10) % lev_fieldx;
8916 y = (random >> 20) % lev_fieldy;
8918 x = RND(lev_fieldx);
8919 y = RND(lev_fieldy);
8921 element = Feld[x][y];
8924 if (!IS_PLAYER(x,y) &&
8925 (element == EL_EMPTY ||
8926 CAN_GROW_INTO(element) ||
8927 element == EL_QUICKSAND_EMPTY ||
8928 element == EL_ACID_SPLASH_LEFT ||
8929 element == EL_ACID_SPLASH_RIGHT))
8931 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8932 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8933 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8934 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8935 Feld[x][y] = EL_AMOEBA_DROP;
8938 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8939 if (!IS_PLAYER(x,y) &&
8940 (element == EL_EMPTY ||
8941 element == EL_SAND ||
8942 element == EL_QUICKSAND_EMPTY ||
8943 element == EL_ACID_SPLASH_LEFT ||
8944 element == EL_ACID_SPLASH_RIGHT))
8946 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8947 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8948 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8949 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8950 Feld[x][y] = EL_AMOEBA_DROP;
8954 random = random * 129 + 1;
8960 if (game.explosions_delayed)
8963 game.explosions_delayed = FALSE;
8965 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8967 element = Feld[x][y];
8969 if (ExplodeField[x][y])
8970 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8971 else if (element == EL_EXPLOSION)
8972 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8974 ExplodeField[x][y] = EX_TYPE_NONE;
8977 game.explosions_delayed = TRUE;
8980 if (game.magic_wall_active)
8982 if (!(game.magic_wall_time_left % 4))
8984 int element = Feld[magic_wall_x][magic_wall_y];
8986 if (element == EL_BD_MAGIC_WALL_FULL ||
8987 element == EL_BD_MAGIC_WALL_ACTIVE ||
8988 element == EL_BD_MAGIC_WALL_EMPTYING)
8989 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8991 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8994 if (game.magic_wall_time_left > 0)
8996 game.magic_wall_time_left--;
8997 if (!game.magic_wall_time_left)
8999 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9001 element = Feld[x][y];
9003 if (element == EL_MAGIC_WALL_ACTIVE ||
9004 element == EL_MAGIC_WALL_FULL)
9006 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9007 DrawLevelField(x, y);
9009 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9010 element == EL_BD_MAGIC_WALL_FULL)
9012 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9013 DrawLevelField(x, y);
9017 game.magic_wall_active = FALSE;
9022 if (game.light_time_left > 0)
9024 game.light_time_left--;
9026 if (game.light_time_left == 0)
9027 RedrawAllLightSwitchesAndInvisibleElements();
9030 if (game.timegate_time_left > 0)
9032 game.timegate_time_left--;
9034 if (game.timegate_time_left == 0)
9035 CloseAllOpenTimegates();
9038 for (i = 0; i < MAX_PLAYERS; i++)
9040 struct PlayerInfo *player = &stored_player[i];
9042 if (SHIELD_ON(player))
9044 if (player->shield_deadly_time_left)
9045 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9046 else if (player->shield_normal_time_left)
9047 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9051 if (TimeFrames >= FRAMES_PER_SECOND)
9056 for (i = 0; i < MAX_PLAYERS; i++)
9058 struct PlayerInfo *player = &stored_player[i];
9060 if (SHIELD_ON(player))
9062 player->shield_normal_time_left--;
9064 if (player->shield_deadly_time_left > 0)
9065 player->shield_deadly_time_left--;
9069 if (!level.use_step_counter)
9077 if (TimeLeft <= 10 && setup.time_limit)
9078 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9080 DrawGameValue_Time(TimeLeft);
9082 if (!TimeLeft && setup.time_limit)
9083 for (i = 0; i < MAX_PLAYERS; i++)
9084 KillHero(&stored_player[i]);
9086 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9087 DrawGameValue_Time(TimePlayed);
9090 if (tape.recording || tape.playing)
9091 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9095 PlayAllPlayersSound();
9097 if (options.debug) /* calculate frames per second */
9099 static unsigned long fps_counter = 0;
9100 static int fps_frames = 0;
9101 unsigned long fps_delay_ms = Counter() - fps_counter;
9105 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9107 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9110 fps_counter = Counter();
9113 redraw_mask |= REDRAW_FPS;
9117 if (stored_player[0].jx != stored_player[0].last_jx ||
9118 stored_player[0].jy != stored_player[0].last_jy)
9119 printf("::: %d, %d, %d, %d, %d\n",
9120 stored_player[0].MovDir,
9121 stored_player[0].MovPos,
9122 stored_player[0].GfxPos,
9123 stored_player[0].Frame,
9124 stored_player[0].StepFrame);
9127 #if USE_NEW_MOVE_DELAY
9128 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9133 for (i = 0; i < MAX_PLAYERS; i++)
9136 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9138 stored_player[i].Frame += move_frames;
9140 if (stored_player[i].MovPos != 0)
9141 stored_player[i].StepFrame += move_frames;
9143 #if USE_NEW_MOVE_DELAY
9144 if (stored_player[i].move_delay > 0)
9145 stored_player[i].move_delay--;
9148 if (stored_player[i].drop_delay > 0)
9149 stored_player[i].drop_delay--;
9154 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9156 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9158 local_player->show_envelope = 0;
9163 /* use random number generator in every frame to make it less predictable */
9164 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9169 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9171 int min_x = x, min_y = y, max_x = x, max_y = y;
9174 for (i = 0; i < MAX_PLAYERS; i++)
9176 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9178 if (!stored_player[i].active || &stored_player[i] == player)
9181 min_x = MIN(min_x, jx);
9182 min_y = MIN(min_y, jy);
9183 max_x = MAX(max_x, jx);
9184 max_y = MAX(max_y, jy);
9187 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9190 static boolean AllPlayersInVisibleScreen()
9194 for (i = 0; i < MAX_PLAYERS; i++)
9196 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9198 if (!stored_player[i].active)
9201 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9208 void ScrollLevel(int dx, int dy)
9210 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9213 BlitBitmap(drawto_field, drawto_field,
9214 FX + TILEX * (dx == -1) - softscroll_offset,
9215 FY + TILEY * (dy == -1) - softscroll_offset,
9216 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9217 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9218 FX + TILEX * (dx == 1) - softscroll_offset,
9219 FY + TILEY * (dy == 1) - softscroll_offset);
9223 x = (dx == 1 ? BX1 : BX2);
9224 for (y = BY1; y <= BY2; y++)
9225 DrawScreenField(x, y);
9230 y = (dy == 1 ? BY1 : BY2);
9231 for (x = BX1; x <= BX2; x++)
9232 DrawScreenField(x, y);
9235 redraw_mask |= REDRAW_FIELD;
9239 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9241 int nextx = x + dx, nexty = y + dy;
9242 int element = Feld[x][y];
9245 element != EL_SP_PORT_LEFT &&
9246 element != EL_SP_GRAVITY_PORT_LEFT &&
9247 element != EL_SP_PORT_HORIZONTAL &&
9248 element != EL_SP_PORT_ANY) ||
9250 element != EL_SP_PORT_RIGHT &&
9251 element != EL_SP_GRAVITY_PORT_RIGHT &&
9252 element != EL_SP_PORT_HORIZONTAL &&
9253 element != EL_SP_PORT_ANY) ||
9255 element != EL_SP_PORT_UP &&
9256 element != EL_SP_GRAVITY_PORT_UP &&
9257 element != EL_SP_PORT_VERTICAL &&
9258 element != EL_SP_PORT_ANY) ||
9260 element != EL_SP_PORT_DOWN &&
9261 element != EL_SP_GRAVITY_PORT_DOWN &&
9262 element != EL_SP_PORT_VERTICAL &&
9263 element != EL_SP_PORT_ANY) ||
9264 !IN_LEV_FIELD(nextx, nexty) ||
9265 !IS_FREE(nextx, nexty))
9272 static boolean canFallDown(struct PlayerInfo *player)
9274 int jx = player->jx, jy = player->jy;
9276 return (IN_LEV_FIELD(jx, jy + 1) &&
9277 (IS_FREE(jx, jy + 1) ||
9278 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9279 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9280 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9283 static boolean canPassField(int x, int y, int move_dir)
9285 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9286 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9287 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9290 int element = Feld[x][y];
9292 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9293 !CAN_MOVE(element) &&
9294 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9295 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9296 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9299 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9301 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9302 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9303 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9307 int nextx = newx + dx;
9308 int nexty = newy + dy;
9312 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9313 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9315 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9317 (IS_DIGGABLE(Feld[newx][newy]) ||
9318 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9319 canPassField(newx, newy, move_dir)));
9322 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9323 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9324 (IS_DIGGABLE(Feld[newx][newy]) ||
9325 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9326 canPassField(newx, newy, move_dir)));
9329 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9330 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9331 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9332 canPassField(newx, newy, move_dir)));
9334 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9335 (IS_DIGGABLE(Feld[newx][newy]) ||
9336 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9337 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9338 !CAN_MOVE(Feld[newx][newy]) &&
9339 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9340 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9341 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9347 static void CheckGravityMovement(struct PlayerInfo *player)
9349 if (game.gravity && !player->programmed_action)
9352 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9353 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9355 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9356 int move_dir_vertical = player->action & MV_VERTICAL;
9360 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9362 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9365 int jx = player->jx, jy = player->jy;
9367 boolean player_is_moving_to_valid_field =
9368 (!player_is_snapping &&
9369 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9370 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9374 (player->last_move_dir & MV_HORIZONTAL ?
9375 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9376 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9380 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9381 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9382 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9383 int new_jx = jx + dx, new_jy = jy + dy;
9384 int nextx = new_jx + dx, nexty = new_jy + dy;
9390 boolean player_can_fall_down = canFallDown(player);
9392 boolean player_can_fall_down =
9393 (IN_LEV_FIELD(jx, jy + 1) &&
9394 (IS_FREE(jx, jy + 1) ||
9395 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9399 boolean player_can_fall_down =
9400 (IN_LEV_FIELD(jx, jy + 1) &&
9401 (IS_FREE(jx, jy + 1)));
9405 boolean player_is_moving_to_valid_field =
9408 !player_is_snapping &&
9412 IN_LEV_FIELD(new_jx, new_jy) &&
9413 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9414 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9415 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9416 IN_LEV_FIELD(nextx, nexty) &&
9417 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9419 IN_LEV_FIELD(new_jx, new_jy) &&
9420 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9421 Feld[new_jx][new_jy] == EL_SAND ||
9422 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9423 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9424 /* !!! extend EL_SAND to anything diggable !!! */
9430 boolean player_is_standing_on_valid_field =
9431 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9432 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9436 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9437 player_can_fall_down,
9438 player_is_standing_on_valid_field,
9439 player_is_moving_to_valid_field,
9440 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9441 player->effective_action,
9442 player->can_fall_into_acid);
9445 if (player_can_fall_down &&
9447 !player_is_standing_on_valid_field &&
9449 !player_is_moving_to_valid_field)
9452 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9453 jx, jy, FrameCounter);
9456 player->programmed_action = MV_DOWN;
9461 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9464 return CheckGravityMovement(player);
9467 if (game.gravity && !player->programmed_action)
9469 int jx = player->jx, jy = player->jy;
9470 boolean field_under_player_is_free =
9471 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9472 boolean player_is_standing_on_valid_field =
9473 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9474 (IS_WALKABLE(Feld[jx][jy]) &&
9475 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9477 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9478 player->programmed_action = MV_DOWN;
9484 -----------------------------------------------------------------------------
9485 dx, dy: direction (non-diagonal) to try to move the player to
9486 real_dx, real_dy: direction as read from input device (can be diagonal)
9489 boolean MovePlayerOneStep(struct PlayerInfo *player,
9490 int dx, int dy, int real_dx, int real_dy)
9493 static int trigger_sides[4][2] =
9495 /* enter side leave side */
9496 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9497 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9498 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9499 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9501 int move_direction = (dx == -1 ? MV_LEFT :
9502 dx == +1 ? MV_RIGHT :
9504 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9505 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9506 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9508 int jx = player->jx, jy = player->jy;
9509 int new_jx = jx + dx, new_jy = jy + dy;
9513 if (!player->active || (!dx && !dy))
9514 return MF_NO_ACTION;
9516 player->MovDir = (dx < 0 ? MV_LEFT :
9519 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9521 if (!IN_LEV_FIELD(new_jx, new_jy))
9522 return MF_NO_ACTION;
9524 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9525 return MF_NO_ACTION;
9528 element = MovingOrBlocked2Element(new_jx, new_jy);
9530 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9533 if (DONT_RUN_INTO(element))
9535 if (element == EL_ACID && dx == 0 && dy == 1)
9537 SplashAcid(new_jx, new_jy);
9538 Feld[jx][jy] = EL_PLAYER_1;
9539 InitMovingField(jx, jy, MV_DOWN);
9540 Store[jx][jy] = EL_ACID;
9541 ContinueMoving(jx, jy);
9545 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9550 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9551 if (can_move != MF_MOVING)
9554 /* check if DigField() has caused relocation of the player */
9555 if (player->jx != jx || player->jy != jy)
9556 return MF_NO_ACTION;
9558 StorePlayer[jx][jy] = 0;
9559 player->last_jx = jx;
9560 player->last_jy = jy;
9561 player->jx = new_jx;
9562 player->jy = new_jy;
9563 StorePlayer[new_jx][new_jy] = player->element_nr;
9566 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9568 player->step_counter++;
9571 player->drop_delay = 0;
9574 PlayerVisit[jx][jy] = FrameCounter;
9576 ScrollPlayer(player, SCROLL_INIT);
9579 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9581 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9583 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9586 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9588 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9589 CE_OTHER_GETS_ENTERED, enter_side);
9590 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9591 CE_ENTERED_BY_PLAYER, enter_side);
9598 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9600 int jx = player->jx, jy = player->jy;
9601 int old_jx = jx, old_jy = jy;
9602 int moved = MF_NO_ACTION;
9605 if (!player->active)
9610 if (player->MovPos == 0)
9612 player->is_moving = FALSE;
9613 player->is_digging = FALSE;
9614 player->is_collecting = FALSE;
9615 player->is_snapping = FALSE;
9616 player->is_pushing = FALSE;
9622 if (!player->active || (!dx && !dy))
9627 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9635 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9636 player->move_delay + player->move_delay_value);
9639 #if USE_NEW_MOVE_DELAY
9640 if (player->move_delay > 0)
9642 if (!FrameReached(&player->move_delay, player->move_delay_value))
9646 printf("::: can NOT move\n");
9652 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9653 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9660 printf("::: COULD move now\n");
9663 #if USE_NEW_MOVE_DELAY
9664 player->move_delay = -1; /* set to "uninitialized" value */
9667 /* store if player is automatically moved to next field */
9668 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9670 /* remove the last programmed player action */
9671 player->programmed_action = 0;
9675 /* should only happen if pre-1.2 tape recordings are played */
9676 /* this is only for backward compatibility */
9678 int original_move_delay_value = player->move_delay_value;
9681 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9685 /* scroll remaining steps with finest movement resolution */
9686 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9688 while (player->MovPos)
9690 ScrollPlayer(player, SCROLL_GO_ON);
9691 ScrollScreen(NULL, SCROLL_GO_ON);
9693 #if USE_NEW_MOVE_DELAY
9694 AdvanceFrameAndPlayerCounters(player->index_nr);
9703 player->move_delay_value = original_move_delay_value;
9706 if (player->last_move_dir & MV_HORIZONTAL)
9708 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9709 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9713 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9714 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9720 if (moved & MF_MOVING && !ScreenMovPos &&
9721 (player == local_player || !options.network))
9723 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9724 int offset = (setup.scroll_delay ? 3 : 0);
9726 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9728 /* actual player has left the screen -- scroll in that direction */
9729 if (jx != old_jx) /* player has moved horizontally */
9730 scroll_x += (jx - old_jx);
9731 else /* player has moved vertically */
9732 scroll_y += (jy - old_jy);
9736 if (jx != old_jx) /* player has moved horizontally */
9738 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9739 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9740 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9742 /* don't scroll over playfield boundaries */
9743 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9744 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9746 /* don't scroll more than one field at a time */
9747 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9749 /* don't scroll against the player's moving direction */
9750 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9751 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9752 scroll_x = old_scroll_x;
9754 else /* player has moved vertically */
9756 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9757 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9758 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9760 /* don't scroll over playfield boundaries */
9761 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9762 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9764 /* don't scroll more than one field at a time */
9765 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9767 /* don't scroll against the player's moving direction */
9768 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9769 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9770 scroll_y = old_scroll_y;
9774 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9776 if (!options.network && !AllPlayersInVisibleScreen())
9778 scroll_x = old_scroll_x;
9779 scroll_y = old_scroll_y;
9783 ScrollScreen(player, SCROLL_INIT);
9784 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9791 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9793 if (!(moved & MF_MOVING) && !player->is_pushing)
9798 player->StepFrame = 0;
9800 if (moved & MF_MOVING)
9803 printf("::: REALLY moves now\n");
9806 if (old_jx != jx && old_jy == jy)
9807 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9808 else if (old_jx == jx && old_jy != jy)
9809 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9811 DrawLevelField(jx, jy); /* for "crumbled sand" */
9813 player->last_move_dir = player->MovDir;
9814 player->is_moving = TRUE;
9816 player->is_snapping = FALSE;
9820 player->is_switching = FALSE;
9823 player->is_dropping = FALSE;
9827 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9830 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9833 int move_direction = player->MovDir;
9835 int enter_side = MV_DIR_OPPOSITE(move_direction);
9836 int leave_side = move_direction;
9838 static int trigger_sides[4][2] =
9840 /* enter side leave side */
9841 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9842 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9843 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9844 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9846 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9847 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9849 int old_element = Feld[old_jx][old_jy];
9850 int new_element = Feld[jx][jy];
9853 /* !!! TEST ONLY !!! */
9854 if (IS_CUSTOM_ELEMENT(old_element))
9855 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9857 player->index_bit, leave_side);
9859 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9861 player->index_bit, leave_side);
9863 if (IS_CUSTOM_ELEMENT(new_element))
9864 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9865 player->index_bit, enter_side);
9867 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9868 CE_OTHER_GETS_ENTERED,
9869 player->index_bit, enter_side);
9879 CheckGravityMovementWhenNotMoving(player);
9882 player->last_move_dir = MV_NO_MOVING;
9884 player->is_moving = FALSE;
9886 #if USE_NEW_MOVE_STYLE
9887 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9888 /* ensure that the player is also allowed to move in the next frame */
9889 /* (currently, the player is forced to wait eight frames before he can try
9892 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9893 player->move_delay = 0; /* allow direct movement in the next frame */
9897 #if USE_NEW_MOVE_DELAY
9898 if (player->move_delay == -1) /* not yet initialized by DigField() */
9899 player->move_delay = player->move_delay_value;
9902 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9904 TestIfHeroTouchesBadThing(jx, jy);
9905 TestIfPlayerTouchesCustomElement(jx, jy);
9908 if (!player->active)
9914 void ScrollPlayer(struct PlayerInfo *player, int mode)
9916 int jx = player->jx, jy = player->jy;
9917 int last_jx = player->last_jx, last_jy = player->last_jy;
9918 int move_stepsize = TILEX / player->move_delay_value;
9920 if (!player->active || !player->MovPos)
9923 if (mode == SCROLL_INIT)
9925 player->actual_frame_counter = FrameCounter;
9926 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9928 #if USE_NEW_BLOCK_STYLE
9929 if (player->block_delay > 0 &&
9930 Feld[last_jx][last_jy] == EL_EMPTY)
9932 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9934 MovDelay[last_jx][last_jy] = player->block_delay + 1;
9936 ChangeDelay[last_jx][last_jy] = player->block_last_field_delay;
9940 #if USE_NEW_MOVE_STYLE
9941 if (player->block_last_field &&
9942 Feld[last_jx][last_jy] == EL_EMPTY)
9943 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9945 if (Feld[last_jx][last_jy] == EL_EMPTY)
9946 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9956 else if (!FrameReached(&player->actual_frame_counter, 1))
9959 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9960 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9962 #if USE_NEW_BLOCK_STYLE
9964 if (!player->block_last_field &&
9965 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9967 RemoveField(last_jx, last_jy);
9969 Feld[last_jx][last_jy] = EL_EMPTY;
9973 /* before DrawPlayer() to draw correct player graphic for this case */
9974 if (player->MovPos == 0)
9975 CheckGravityMovement(player);
9978 DrawPlayer(player); /* needed here only to cleanup last field */
9981 if (player->MovPos == 0) /* player reached destination field */
9984 if (player->move_delay_reset_counter > 0)
9986 player->move_delay_reset_counter--;
9988 if (player->move_delay_reset_counter == 0)
9990 /* continue with normal speed after quickly moving through gate */
9991 HALVE_PLAYER_SPEED(player);
9993 /* be able to make the next move without delay */
9994 player->move_delay = 0;
9998 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10000 /* continue with normal speed after quickly moving through gate */
10001 HALVE_PLAYER_SPEED(player);
10003 /* be able to make the next move without delay */
10004 player->move_delay = 0;
10008 #if USE_NEW_BLOCK_STYLE
10010 if (player->block_last_field &&
10011 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10013 RemoveField(last_jx, last_jy);
10015 Feld[last_jx][last_jy] = EL_EMPTY;
10019 player->last_jx = jx;
10020 player->last_jy = jy;
10022 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10023 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10024 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10026 DrawPlayer(player); /* needed here only to cleanup last field */
10027 RemoveHero(player);
10029 if (local_player->friends_still_needed == 0 ||
10030 IS_SP_ELEMENT(Feld[jx][jy]))
10031 player->LevelSolved = player->GameOver = TRUE;
10035 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10036 /* this breaks one level: "machine", level 000 */
10038 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10041 int move_direction = player->MovDir;
10043 int enter_side = MV_DIR_OPPOSITE(move_direction);
10044 int leave_side = move_direction;
10046 static int trigger_sides[4][2] =
10048 /* enter side leave side */
10049 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10050 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10051 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10052 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10054 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10055 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10057 int old_jx = last_jx;
10058 int old_jy = last_jy;
10059 int old_element = Feld[old_jx][old_jy];
10060 int new_element = Feld[jx][jy];
10063 /* !!! TEST ONLY !!! */
10064 if (IS_CUSTOM_ELEMENT(old_element))
10065 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10067 player->index_bit, leave_side);
10069 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10070 CE_OTHER_GETS_LEFT,
10071 player->index_bit, leave_side);
10073 if (IS_CUSTOM_ELEMENT(new_element))
10074 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10075 player->index_bit, enter_side);
10077 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10078 CE_OTHER_GETS_ENTERED,
10079 player->index_bit, enter_side);
10085 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10087 TestIfHeroTouchesBadThing(jx, jy);
10088 TestIfPlayerTouchesCustomElement(jx, jy);
10091 /* needed because pushed element has not yet reached its destination,
10092 so it would trigger a change event at its previous field location */
10093 if (!player->is_pushing)
10095 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10098 if (!player->active)
10099 RemoveHero(player);
10102 if (level.use_step_counter)
10112 if (TimeLeft <= 10 && setup.time_limit)
10113 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10115 DrawGameValue_Time(TimeLeft);
10117 if (!TimeLeft && setup.time_limit)
10118 for (i = 0; i < MAX_PLAYERS; i++)
10119 KillHero(&stored_player[i]);
10121 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10122 DrawGameValue_Time(TimePlayed);
10125 if (tape.single_step && tape.recording && !tape.pausing &&
10126 !player->programmed_action)
10127 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10131 void ScrollScreen(struct PlayerInfo *player, int mode)
10133 static unsigned long screen_frame_counter = 0;
10135 if (mode == SCROLL_INIT)
10137 /* set scrolling step size according to actual player's moving speed */
10138 ScrollStepSize = TILEX / player->move_delay_value;
10140 screen_frame_counter = FrameCounter;
10141 ScreenMovDir = player->MovDir;
10142 ScreenMovPos = player->MovPos;
10143 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10146 else if (!FrameReached(&screen_frame_counter, 1))
10151 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10152 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10153 redraw_mask |= REDRAW_FIELD;
10156 ScreenMovDir = MV_NO_MOVING;
10159 void TestIfPlayerTouchesCustomElement(int x, int y)
10161 static int xy[4][2] =
10168 static int trigger_sides[4][2] =
10170 /* center side border side */
10171 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10172 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10173 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10174 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10176 static int touch_dir[4] =
10178 MV_LEFT | MV_RIGHT,
10183 int center_element = Feld[x][y]; /* should always be non-moving! */
10186 for (i = 0; i < NUM_DIRECTIONS; i++)
10188 int xx = x + xy[i][0];
10189 int yy = y + xy[i][1];
10190 int center_side = trigger_sides[i][0];
10191 int border_side = trigger_sides[i][1];
10192 int border_element;
10194 if (!IN_LEV_FIELD(xx, yy))
10197 if (IS_PLAYER(x, y))
10199 struct PlayerInfo *player = PLAYERINFO(x, y);
10201 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10202 border_element = Feld[xx][yy]; /* may be moving! */
10203 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10204 border_element = Feld[xx][yy];
10205 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10206 border_element = MovingOrBlocked2Element(xx, yy);
10208 continue; /* center and border element do not touch */
10211 /* !!! TEST ONLY !!! */
10212 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10213 player->index_bit, border_side);
10214 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10215 CE_OTHER_GETS_TOUCHED,
10216 player->index_bit, border_side);
10218 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10219 CE_OTHER_GETS_TOUCHED,
10220 player->index_bit, border_side);
10221 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10222 player->index_bit, border_side);
10225 else if (IS_PLAYER(xx, yy))
10227 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10229 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10231 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10232 continue; /* center and border element do not touch */
10236 /* !!! TEST ONLY !!! */
10237 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10238 player->index_bit, center_side);
10239 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10240 CE_OTHER_GETS_TOUCHED,
10241 player->index_bit, center_side);
10243 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10244 CE_OTHER_GETS_TOUCHED,
10245 player->index_bit, center_side);
10246 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10247 player->index_bit, center_side);
10255 void TestIfElementTouchesCustomElement(int x, int y)
10257 static int xy[4][2] =
10264 static int trigger_sides[4][2] =
10266 /* center side border side */
10267 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10268 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10269 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10270 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10272 static int touch_dir[4] =
10274 MV_LEFT | MV_RIGHT,
10279 boolean change_center_element = FALSE;
10280 int center_element_change_page = 0;
10281 int center_element = Feld[x][y]; /* should always be non-moving! */
10282 int border_trigger_element = EL_UNDEFINED;
10285 for (i = 0; i < NUM_DIRECTIONS; i++)
10287 int xx = x + xy[i][0];
10288 int yy = y + xy[i][1];
10289 int center_side = trigger_sides[i][0];
10290 int border_side = trigger_sides[i][1];
10291 int border_element;
10293 if (!IN_LEV_FIELD(xx, yy))
10296 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10297 border_element = Feld[xx][yy]; /* may be moving! */
10298 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10299 border_element = Feld[xx][yy];
10300 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10301 border_element = MovingOrBlocked2Element(xx, yy);
10303 continue; /* center and border element do not touch */
10305 /* check for change of center element (but change it only once) */
10306 if (IS_CUSTOM_ELEMENT(center_element) &&
10307 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10308 !change_center_element)
10310 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10312 struct ElementChangeInfo *change =
10313 &element_info[center_element].change_page[j];
10315 if (change->can_change &&
10316 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10317 change->trigger_side & border_side &&
10319 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10321 change->trigger_element == border_element
10325 change_center_element = TRUE;
10326 center_element_change_page = j;
10327 border_trigger_element = border_element;
10334 /* check for change of border element */
10335 if (IS_CUSTOM_ELEMENT(border_element) &&
10336 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10338 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10340 struct ElementChangeInfo *change =
10341 &element_info[border_element].change_page[j];
10343 if (change->can_change &&
10344 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10345 change->trigger_side & center_side &&
10347 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10349 change->trigger_element == center_element
10354 printf("::: border_element %d, %d\n", x, y);
10357 CheckElementChangeByPage(xx, yy, border_element, center_element,
10358 CE_OTHER_IS_TOUCHING, j);
10365 if (change_center_element)
10368 printf("::: center_element %d, %d\n", x, y);
10371 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10372 CE_OTHER_IS_TOUCHING, center_element_change_page);
10376 void TestIfElementHitsCustomElement(int x, int y, int direction)
10378 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10379 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10380 int hitx = x + dx, hity = y + dy;
10381 int hitting_element = Feld[x][y];
10382 int touched_element;
10384 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10385 !IS_FREE(hitx, hity) &&
10386 (!IS_MOVING(hitx, hity) ||
10387 MovDir[hitx][hity] != direction ||
10388 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10391 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10395 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10399 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10400 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10402 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10403 CE_HITTING_SOMETHING, direction);
10405 if (IN_LEV_FIELD(hitx, hity))
10407 int opposite_direction = MV_DIR_OPPOSITE(direction);
10408 int hitting_side = direction;
10409 int touched_side = opposite_direction;
10411 int touched_element = MovingOrBlocked2Element(hitx, hity);
10414 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10415 MovDir[hitx][hity] != direction ||
10416 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10425 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10426 CE_HIT_BY_SOMETHING, opposite_direction);
10428 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10429 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10431 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10433 struct ElementChangeInfo *change =
10434 &element_info[hitting_element].change_page[i];
10436 if (change->can_change &&
10437 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10438 change->trigger_side & touched_side &&
10441 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10443 change->trigger_element == touched_element
10447 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10448 CE_OTHER_IS_HITTING, i);
10454 if (IS_CUSTOM_ELEMENT(touched_element) &&
10455 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10457 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10459 struct ElementChangeInfo *change =
10460 &element_info[touched_element].change_page[i];
10462 if (change->can_change &&
10463 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10464 change->trigger_side & hitting_side &&
10466 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10468 change->trigger_element == hitting_element
10472 CheckElementChangeByPage(hitx, hity, touched_element,
10473 hitting_element, CE_OTHER_GETS_HIT, i);
10483 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10485 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10486 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10487 int hitx = x + dx, hity = y + dy;
10488 int hitting_element = Feld[x][y];
10489 int touched_element;
10491 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10492 !IS_FREE(hitx, hity) &&
10493 (!IS_MOVING(hitx, hity) ||
10494 MovDir[hitx][hity] != direction ||
10495 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10498 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10502 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10506 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10507 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10509 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10510 EP_CAN_SMASH_EVERYTHING, direction);
10512 if (IN_LEV_FIELD(hitx, hity))
10514 int opposite_direction = MV_DIR_OPPOSITE(direction);
10515 int hitting_side = direction;
10516 int touched_side = opposite_direction;
10518 int touched_element = MovingOrBlocked2Element(hitx, hity);
10521 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10522 MovDir[hitx][hity] != direction ||
10523 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10532 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10533 CE_SMASHED_BY_SOMETHING, opposite_direction);
10535 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10536 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10538 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10540 struct ElementChangeInfo *change =
10541 &element_info[hitting_element].change_page[i];
10543 if (change->can_change &&
10544 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10545 change->trigger_side & touched_side &&
10548 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10550 change->trigger_element == touched_element
10554 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10555 CE_OTHER_IS_SMASHING, i);
10561 if (IS_CUSTOM_ELEMENT(touched_element) &&
10562 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10564 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10566 struct ElementChangeInfo *change =
10567 &element_info[touched_element].change_page[i];
10569 if (change->can_change &&
10570 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10571 change->trigger_side & hitting_side &&
10573 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10575 change->trigger_element == hitting_element
10579 CheckElementChangeByPage(hitx, hity, touched_element,
10580 hitting_element, CE_OTHER_GETS_SMASHED,i);
10590 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10592 int i, kill_x = -1, kill_y = -1;
10593 int bad_element = -1;
10594 static int test_xy[4][2] =
10601 static int test_dir[4] =
10609 for (i = 0; i < NUM_DIRECTIONS; i++)
10611 int test_x, test_y, test_move_dir, test_element;
10613 test_x = good_x + test_xy[i][0];
10614 test_y = good_y + test_xy[i][1];
10616 if (!IN_LEV_FIELD(test_x, test_y))
10620 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10623 test_element = Feld[test_x][test_y];
10625 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10628 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10629 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10631 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10632 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10636 bad_element = test_element;
10642 if (kill_x != -1 || kill_y != -1)
10644 if (IS_PLAYER(good_x, good_y))
10646 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10649 if (player->shield_deadly_time_left > 0 &&
10650 !IS_INDESTRUCTIBLE(bad_element))
10651 Bang(kill_x, kill_y);
10652 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10655 if (player->shield_deadly_time_left > 0)
10656 Bang(kill_x, kill_y);
10657 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10662 Bang(good_x, good_y);
10666 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10668 int i, kill_x = -1, kill_y = -1;
10669 int bad_element = Feld[bad_x][bad_y];
10670 static int test_xy[4][2] =
10677 static int touch_dir[4] =
10679 MV_LEFT | MV_RIGHT,
10684 static int test_dir[4] =
10692 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10695 for (i = 0; i < NUM_DIRECTIONS; i++)
10697 int test_x, test_y, test_move_dir, test_element;
10699 test_x = bad_x + test_xy[i][0];
10700 test_y = bad_y + test_xy[i][1];
10701 if (!IN_LEV_FIELD(test_x, test_y))
10705 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10707 test_element = Feld[test_x][test_y];
10709 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10710 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10712 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10713 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10715 /* good thing is player or penguin that does not move away */
10716 if (IS_PLAYER(test_x, test_y))
10718 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10720 if (bad_element == EL_ROBOT && player->is_moving)
10721 continue; /* robot does not kill player if he is moving */
10723 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10725 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10726 continue; /* center and border element do not touch */
10733 else if (test_element == EL_PENGUIN)
10742 if (kill_x != -1 || kill_y != -1)
10744 if (IS_PLAYER(kill_x, kill_y))
10746 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10749 if (player->shield_deadly_time_left > 0 &&
10750 !IS_INDESTRUCTIBLE(bad_element))
10751 Bang(bad_x, bad_y);
10752 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10755 if (player->shield_deadly_time_left > 0)
10756 Bang(bad_x, bad_y);
10757 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10762 Bang(kill_x, kill_y);
10766 void TestIfHeroTouchesBadThing(int x, int y)
10768 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10771 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10773 TestIfGoodThingHitsBadThing(x, y, move_dir);
10776 void TestIfBadThingTouchesHero(int x, int y)
10778 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10781 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10783 TestIfBadThingHitsGoodThing(x, y, move_dir);
10786 void TestIfFriendTouchesBadThing(int x, int y)
10788 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10791 void TestIfBadThingTouchesFriend(int x, int y)
10793 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10796 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10798 int i, kill_x = bad_x, kill_y = bad_y;
10799 static int xy[4][2] =
10807 for (i = 0; i < NUM_DIRECTIONS; i++)
10811 x = bad_x + xy[i][0];
10812 y = bad_y + xy[i][1];
10813 if (!IN_LEV_FIELD(x, y))
10816 element = Feld[x][y];
10817 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10818 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10826 if (kill_x != bad_x || kill_y != bad_y)
10827 Bang(bad_x, bad_y);
10830 void KillHero(struct PlayerInfo *player)
10832 int jx = player->jx, jy = player->jy;
10834 if (!player->active)
10837 /* remove accessible field at the player's position */
10838 Feld[jx][jy] = EL_EMPTY;
10840 /* deactivate shield (else Bang()/Explode() would not work right) */
10841 player->shield_normal_time_left = 0;
10842 player->shield_deadly_time_left = 0;
10848 static void KillHeroUnlessEnemyProtected(int x, int y)
10850 if (!PLAYER_ENEMY_PROTECTED(x, y))
10851 KillHero(PLAYERINFO(x, y));
10854 static void KillHeroUnlessExplosionProtected(int x, int y)
10856 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10857 KillHero(PLAYERINFO(x, y));
10860 void BuryHero(struct PlayerInfo *player)
10862 int jx = player->jx, jy = player->jy;
10864 if (!player->active)
10868 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10870 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10872 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10874 player->GameOver = TRUE;
10875 RemoveHero(player);
10878 void RemoveHero(struct PlayerInfo *player)
10880 int jx = player->jx, jy = player->jy;
10881 int i, found = FALSE;
10883 player->present = FALSE;
10884 player->active = FALSE;
10886 if (!ExplodeField[jx][jy])
10887 StorePlayer[jx][jy] = 0;
10889 for (i = 0; i < MAX_PLAYERS; i++)
10890 if (stored_player[i].active)
10894 AllPlayersGone = TRUE;
10901 =============================================================================
10902 checkDiagonalPushing()
10903 -----------------------------------------------------------------------------
10904 check if diagonal input device direction results in pushing of object
10905 (by checking if the alternative direction is walkable, diggable, ...)
10906 =============================================================================
10909 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10910 int x, int y, int real_dx, int real_dy)
10912 int jx, jy, dx, dy, xx, yy;
10914 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10917 /* diagonal direction: check alternative direction */
10922 xx = jx + (dx == 0 ? real_dx : 0);
10923 yy = jy + (dy == 0 ? real_dy : 0);
10925 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10929 =============================================================================
10931 -----------------------------------------------------------------------------
10932 x, y: field next to player (non-diagonal) to try to dig to
10933 real_dx, real_dy: direction as read from input device (can be diagonal)
10934 =============================================================================
10937 int DigField(struct PlayerInfo *player,
10938 int oldx, int oldy, int x, int y,
10939 int real_dx, int real_dy, int mode)
10942 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10944 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10945 boolean player_was_pushing = player->is_pushing;
10946 int jx = oldx, jy = oldy;
10947 int dx = x - jx, dy = y - jy;
10948 int nextx = x + dx, nexty = y + dy;
10949 int move_direction = (dx == -1 ? MV_LEFT :
10950 dx == +1 ? MV_RIGHT :
10952 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10953 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10955 int dig_side = MV_DIR_OPPOSITE(move_direction);
10957 static int trigger_sides[4] =
10959 CH_SIDE_RIGHT, /* moving left */
10960 CH_SIDE_LEFT, /* moving right */
10961 CH_SIDE_BOTTOM, /* moving up */
10962 CH_SIDE_TOP, /* moving down */
10964 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10966 int old_element = Feld[jx][jy];
10969 if (is_player) /* function can also be called by EL_PENGUIN */
10971 if (player->MovPos == 0)
10973 player->is_digging = FALSE;
10974 player->is_collecting = FALSE;
10977 if (player->MovPos == 0) /* last pushing move finished */
10978 player->is_pushing = FALSE;
10980 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10982 player->is_switching = FALSE;
10983 #if USE_NEW_PUSH_DELAY
10984 player->push_delay = -1;
10986 player->push_delay = 0;
10989 return MF_NO_ACTION;
10993 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10994 return MF_NO_ACTION;
10999 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11001 if (IS_TUBE(Feld[jx][jy]) ||
11002 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11006 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11007 int tube_leave_directions[][2] =
11009 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11010 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11011 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11012 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11013 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11014 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11015 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11016 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11017 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11018 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11019 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11020 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11023 while (tube_leave_directions[i][0] != tube_element)
11026 if (tube_leave_directions[i][0] == -1) /* should not happen */
11030 if (!(tube_leave_directions[i][1] & move_direction))
11031 return MF_NO_ACTION; /* tube has no opening in this direction */
11036 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11037 old_element = Back[jx][jy];
11041 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11042 return MF_NO_ACTION; /* field has no opening in this direction */
11044 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11045 return MF_NO_ACTION; /* field has no opening in this direction */
11047 element = Feld[x][y];
11049 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11050 return MF_NO_ACTION;
11052 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11053 game.engine_version >= VERSION_IDENT(2,2,0,0))
11054 return MF_NO_ACTION;
11057 if (game.gravity && is_player && !player->is_auto_moving &&
11058 canFallDown(player) && move_direction != MV_DOWN &&
11059 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11060 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11064 if (element == EL_EMPTY_SPACE &&
11065 game.gravity && !player->is_auto_moving &&
11066 canFallDown(player) && move_direction != MV_DOWN)
11067 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11073 case EL_SP_PORT_LEFT:
11074 case EL_SP_PORT_RIGHT:
11075 case EL_SP_PORT_UP:
11076 case EL_SP_PORT_DOWN:
11077 case EL_SP_PORT_HORIZONTAL:
11078 case EL_SP_PORT_VERTICAL:
11079 case EL_SP_PORT_ANY:
11080 case EL_SP_GRAVITY_PORT_LEFT:
11081 case EL_SP_GRAVITY_PORT_RIGHT:
11082 case EL_SP_GRAVITY_PORT_UP:
11083 case EL_SP_GRAVITY_PORT_DOWN:
11085 if (!canEnterSupaplexPort(x, y, dx, dy))
11086 return MF_NO_ACTION;
11089 element != EL_SP_PORT_LEFT &&
11090 element != EL_SP_GRAVITY_PORT_LEFT &&
11091 element != EL_SP_PORT_HORIZONTAL &&
11092 element != EL_SP_PORT_ANY) ||
11094 element != EL_SP_PORT_RIGHT &&
11095 element != EL_SP_GRAVITY_PORT_RIGHT &&
11096 element != EL_SP_PORT_HORIZONTAL &&
11097 element != EL_SP_PORT_ANY) ||
11099 element != EL_SP_PORT_UP &&
11100 element != EL_SP_GRAVITY_PORT_UP &&
11101 element != EL_SP_PORT_VERTICAL &&
11102 element != EL_SP_PORT_ANY) ||
11104 element != EL_SP_PORT_DOWN &&
11105 element != EL_SP_GRAVITY_PORT_DOWN &&
11106 element != EL_SP_PORT_VERTICAL &&
11107 element != EL_SP_PORT_ANY) ||
11108 !IN_LEV_FIELD(nextx, nexty) ||
11109 !IS_FREE(nextx, nexty))
11110 return MF_NO_ACTION;
11113 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11114 element == EL_SP_GRAVITY_PORT_RIGHT ||
11115 element == EL_SP_GRAVITY_PORT_UP ||
11116 element == EL_SP_GRAVITY_PORT_DOWN)
11117 game.gravity = !game.gravity;
11119 /* automatically move to the next field with double speed */
11120 player->programmed_action = move_direction;
11122 if (player->move_delay_reset_counter == 0)
11124 player->move_delay_reset_counter = 2; /* two double speed steps */
11126 DOUBLE_PLAYER_SPEED(player);
11129 player->move_delay_reset_counter = 2;
11131 DOUBLE_PLAYER_SPEED(player);
11135 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11138 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11144 case EL_TUBE_VERTICAL:
11145 case EL_TUBE_HORIZONTAL:
11146 case EL_TUBE_VERTICAL_LEFT:
11147 case EL_TUBE_VERTICAL_RIGHT:
11148 case EL_TUBE_HORIZONTAL_UP:
11149 case EL_TUBE_HORIZONTAL_DOWN:
11150 case EL_TUBE_LEFT_UP:
11151 case EL_TUBE_LEFT_DOWN:
11152 case EL_TUBE_RIGHT_UP:
11153 case EL_TUBE_RIGHT_DOWN:
11156 int tube_enter_directions[][2] =
11158 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11159 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11160 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11161 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11162 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11163 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11164 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11165 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11166 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11167 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11168 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11169 { -1, MV_NO_MOVING }
11172 while (tube_enter_directions[i][0] != element)
11175 if (tube_enter_directions[i][0] == -1) /* should not happen */
11179 if (!(tube_enter_directions[i][1] & move_direction))
11180 return MF_NO_ACTION; /* tube has no opening in this direction */
11182 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11190 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11192 if (IS_WALKABLE(element))
11195 int sound_element = SND_ELEMENT(element);
11196 int sound_action = ACTION_WALKING;
11199 if (!ACCESS_FROM(element, opposite_direction))
11200 return MF_NO_ACTION; /* field not accessible from this direction */
11204 if (element == EL_EMPTY_SPACE &&
11205 game.gravity && !player->is_auto_moving &&
11206 canFallDown(player) && move_direction != MV_DOWN)
11207 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11210 if (IS_GATE(element))
11212 if (!player->key[element - EL_GATE_1])
11213 return MF_NO_ACTION;
11215 else if (IS_GATE_GRAY(element))
11217 if (!player->key[element - EL_GATE_1_GRAY])
11218 return MF_NO_ACTION;
11220 else if (element == EL_EXIT_OPEN ||
11221 element == EL_SP_EXIT_OPEN ||
11222 element == EL_SP_EXIT_OPENING)
11224 sound_action = ACTION_PASSING; /* player is passing exit */
11226 else if (element == EL_EMPTY)
11228 sound_action = ACTION_MOVING; /* nothing to walk on */
11231 /* play sound from background or player, whatever is available */
11232 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11233 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11235 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11240 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11242 else if (IS_PASSABLE(element))
11246 if (!canPassField(x, y, move_direction))
11247 return MF_NO_ACTION;
11252 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11253 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11254 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11255 return MF_NO_ACTION;
11257 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11258 return MF_NO_ACTION;
11263 if (!ACCESS_FROM(element, opposite_direction))
11264 return MF_NO_ACTION; /* field not accessible from this direction */
11266 if (IS_CUSTOM_ELEMENT(element) &&
11267 !ACCESS_FROM(element, opposite_direction))
11268 return MF_NO_ACTION; /* field not accessible from this direction */
11272 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11273 return MF_NO_ACTION;
11278 if (IS_EM_GATE(element))
11280 if (!player->key[element - EL_EM_GATE_1])
11281 return MF_NO_ACTION;
11283 else if (IS_EM_GATE_GRAY(element))
11285 if (!player->key[element - EL_EM_GATE_1_GRAY])
11286 return MF_NO_ACTION;
11288 else if (IS_SP_PORT(element))
11290 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11291 element == EL_SP_GRAVITY_PORT_RIGHT ||
11292 element == EL_SP_GRAVITY_PORT_UP ||
11293 element == EL_SP_GRAVITY_PORT_DOWN)
11294 game.gravity = !game.gravity;
11295 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11296 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11297 element == EL_SP_GRAVITY_ON_PORT_UP ||
11298 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11299 game.gravity = TRUE;
11300 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11301 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11302 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11303 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11304 game.gravity = FALSE;
11307 /* automatically move to the next field with double speed */
11308 player->programmed_action = move_direction;
11310 if (player->move_delay_reset_counter == 0)
11312 player->move_delay_reset_counter = 2; /* two double speed steps */
11314 DOUBLE_PLAYER_SPEED(player);
11317 player->move_delay_reset_counter = 2;
11319 DOUBLE_PLAYER_SPEED(player);
11322 PlayLevelSoundAction(x, y, ACTION_PASSING);
11326 else if (IS_DIGGABLE(element))
11330 if (mode != DF_SNAP)
11333 GfxElement[x][y] = GFX_ELEMENT(element);
11336 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11338 player->is_digging = TRUE;
11341 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11343 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11344 player->index_bit, dig_side);
11347 if (mode == DF_SNAP)
11348 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11353 else if (IS_COLLECTIBLE(element))
11357 if (is_player && mode != DF_SNAP)
11359 GfxElement[x][y] = element;
11360 player->is_collecting = TRUE;
11363 if (element == EL_SPEED_PILL)
11364 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11365 else if (element == EL_EXTRA_TIME && level.time > 0)
11368 DrawGameValue_Time(TimeLeft);
11370 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11372 player->shield_normal_time_left += 10;
11373 if (element == EL_SHIELD_DEADLY)
11374 player->shield_deadly_time_left += 10;
11376 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11378 if (player->inventory_size < MAX_INVENTORY_SIZE)
11379 player->inventory_element[player->inventory_size++] = element;
11381 DrawGameValue_Dynamite(local_player->inventory_size);
11383 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11385 player->dynabomb_count++;
11386 player->dynabombs_left++;
11388 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11390 player->dynabomb_size++;
11392 else if (element == EL_DYNABOMB_INCREASE_POWER)
11394 player->dynabomb_xl = TRUE;
11396 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11397 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11399 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11400 element - EL_KEY_1 : element - EL_EM_KEY_1);
11402 player->key[key_nr] = TRUE;
11404 DrawGameValue_Keys(player);
11406 redraw_mask |= REDRAW_DOOR_1;
11408 else if (IS_ENVELOPE(element))
11411 player->show_envelope = element;
11413 ShowEnvelope(element - EL_ENVELOPE_1);
11416 else if (IS_DROPPABLE(element) ||
11417 IS_THROWABLE(element)) /* can be collected and dropped */
11421 if (element_info[element].collect_count == 0)
11422 player->inventory_infinite_element = element;
11424 for (i = 0; i < element_info[element].collect_count; i++)
11425 if (player->inventory_size < MAX_INVENTORY_SIZE)
11426 player->inventory_element[player->inventory_size++] = element;
11428 DrawGameValue_Dynamite(local_player->inventory_size);
11430 else if (element_info[element].collect_count > 0)
11432 local_player->gems_still_needed -=
11433 element_info[element].collect_count;
11434 if (local_player->gems_still_needed < 0)
11435 local_player->gems_still_needed = 0;
11437 DrawGameValue_Emeralds(local_player->gems_still_needed);
11440 RaiseScoreElement(element);
11441 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11444 CheckTriggeredElementChangeByPlayer(x, y, element,
11445 CE_OTHER_GETS_COLLECTED,
11446 player->index_bit, dig_side);
11449 if (mode == DF_SNAP)
11450 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11455 else if (IS_PUSHABLE(element))
11457 if (mode == DF_SNAP && element != EL_BD_ROCK)
11458 return MF_NO_ACTION;
11460 if (CAN_FALL(element) && dy)
11461 return MF_NO_ACTION;
11463 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11464 !(element == EL_SPRING && level.use_spring_bug))
11465 return MF_NO_ACTION;
11468 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11469 ((move_direction & MV_VERTICAL &&
11470 ((element_info[element].move_pattern & MV_LEFT &&
11471 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11472 (element_info[element].move_pattern & MV_RIGHT &&
11473 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11474 (move_direction & MV_HORIZONTAL &&
11475 ((element_info[element].move_pattern & MV_UP &&
11476 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11477 (element_info[element].move_pattern & MV_DOWN &&
11478 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11479 return MF_NO_ACTION;
11483 /* do not push elements already moving away faster than player */
11484 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11485 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11486 return MF_NO_ACTION;
11488 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11489 return MF_NO_ACTION;
11495 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11497 if (player->push_delay_value == -1 || !player_was_pushing)
11498 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11500 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11502 if (player->push_delay_value == -1)
11503 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11506 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11508 if (player->push_delay_value == -1 || !player_was_pushing)
11509 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11512 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11514 if (!player->is_pushing)
11515 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11519 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11520 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11521 !player_is_pushing))
11522 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11525 if (!player->is_pushing &&
11526 game.engine_version >= VERSION_IDENT(2,2,0,7))
11527 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11531 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11532 player->push_delay, player->push_delay_value,
11533 FrameCounter, game.engine_version,
11534 player_was_pushing, player->is_pushing,
11535 element, element_info[element].token_name,
11536 GET_NEW_PUSH_DELAY(element));
11539 player->is_pushing = TRUE;
11541 if (!(IN_LEV_FIELD(nextx, nexty) &&
11542 (IS_FREE(nextx, nexty) ||
11543 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11544 IS_SB_ELEMENT(element)))))
11545 return MF_NO_ACTION;
11547 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11548 return MF_NO_ACTION;
11550 #if USE_NEW_PUSH_DELAY
11553 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11554 printf("::: ALERT: %d, %d [%d / %d]\n",
11555 player->push_delay, player->push_delay2,
11556 FrameCounter, FrameCounter / 50);
11559 if (player->push_delay == -1) /* new pushing; restart delay */
11560 player->push_delay = 0;
11562 if (player->push_delay == 0) /* new pushing; restart delay */
11563 player->push_delay = FrameCounter;
11566 #if USE_NEW_PUSH_DELAY
11568 if ( (player->push_delay > 0) != (!xxx_fr) )
11569 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11570 player->push_delay,
11571 xxx_pdv2, player->push_delay2, player->push_delay_value,
11572 FrameCounter, FrameCounter / 50);
11576 if (player->push_delay > 0 &&
11577 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11578 element != EL_SPRING && element != EL_BALLOON)
11581 if (player->push_delay < player->push_delay_value &&
11582 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11583 element != EL_SPRING && element != EL_BALLOON)
11587 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11588 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11589 element != EL_SPRING && element != EL_BALLOON)
11592 /* make sure that there is no move delay before next try to push */
11593 #if USE_NEW_MOVE_DELAY
11594 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11595 player->move_delay = 0;
11597 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11598 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11601 return MF_NO_ACTION;
11605 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11608 if (IS_SB_ELEMENT(element))
11610 if (element == EL_SOKOBAN_FIELD_FULL)
11612 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11613 local_player->sokobanfields_still_needed++;
11616 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11618 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11619 local_player->sokobanfields_still_needed--;
11622 Feld[x][y] = EL_SOKOBAN_OBJECT;
11624 if (Back[x][y] == Back[nextx][nexty])
11625 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11626 else if (Back[x][y] != 0)
11627 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11630 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11633 if (local_player->sokobanfields_still_needed == 0 &&
11634 game.emulation == EMU_SOKOBAN)
11636 player->LevelSolved = player->GameOver = TRUE;
11637 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11641 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11643 InitMovingField(x, y, move_direction);
11644 GfxAction[x][y] = ACTION_PUSHING;
11646 if (mode == DF_SNAP)
11647 ContinueMoving(x, y);
11649 MovPos[x][y] = (dx != 0 ? dx : dy);
11651 Pushed[x][y] = TRUE;
11652 Pushed[nextx][nexty] = TRUE;
11654 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11655 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11657 player->push_delay_value = -1; /* get new value later */
11660 /* check for element change _after_ element has been pushed! */
11664 /* !!! TEST ONLY !!! */
11665 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11666 player->index_bit, dig_side);
11667 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11668 player->index_bit, dig_side);
11670 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11671 player->index_bit, dig_side);
11672 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11673 player->index_bit, dig_side);
11679 else if (IS_SWITCHABLE(element))
11681 if (PLAYER_SWITCHING(player, x, y))
11683 CheckTriggeredElementChangeByPlayer(x,y, element,
11684 CE_OTHER_GETS_PRESSED,
11685 player->index_bit, dig_side);
11690 player->is_switching = TRUE;
11691 player->switch_x = x;
11692 player->switch_y = y;
11694 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11696 if (element == EL_ROBOT_WHEEL)
11698 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11702 DrawLevelField(x, y);
11704 else if (element == EL_SP_TERMINAL)
11708 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11710 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11712 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11713 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11716 else if (IS_BELT_SWITCH(element))
11718 ToggleBeltSwitch(x, y);
11720 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11721 element == EL_SWITCHGATE_SWITCH_DOWN)
11723 ToggleSwitchgateSwitch(x, y);
11725 else if (element == EL_LIGHT_SWITCH ||
11726 element == EL_LIGHT_SWITCH_ACTIVE)
11728 ToggleLightSwitch(x, y);
11731 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11732 SND_LIGHT_SWITCH_ACTIVATING :
11733 SND_LIGHT_SWITCH_DEACTIVATING);
11736 else if (element == EL_TIMEGATE_SWITCH)
11738 ActivateTimegateSwitch(x, y);
11740 else if (element == EL_BALLOON_SWITCH_LEFT ||
11741 element == EL_BALLOON_SWITCH_RIGHT ||
11742 element == EL_BALLOON_SWITCH_UP ||
11743 element == EL_BALLOON_SWITCH_DOWN ||
11744 element == EL_BALLOON_SWITCH_ANY)
11746 if (element == EL_BALLOON_SWITCH_ANY)
11747 game.balloon_dir = move_direction;
11749 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11750 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11751 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11752 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11755 else if (element == EL_LAMP)
11757 Feld[x][y] = EL_LAMP_ACTIVE;
11758 local_player->lights_still_needed--;
11760 DrawLevelField(x, y);
11762 else if (element == EL_TIME_ORB_FULL)
11764 Feld[x][y] = EL_TIME_ORB_EMPTY;
11766 DrawGameValue_Time(TimeLeft);
11768 DrawLevelField(x, y);
11771 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11775 CheckTriggeredElementChangeByPlayer(x, y, element,
11776 CE_OTHER_IS_SWITCHING,
11777 player->index_bit, dig_side);
11779 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11780 player->index_bit, dig_side);
11786 if (!PLAYER_SWITCHING(player, x, y))
11788 player->is_switching = TRUE;
11789 player->switch_x = x;
11790 player->switch_y = y;
11793 /* !!! TEST ONLY !!! */
11794 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11795 player->index_bit, dig_side);
11796 CheckTriggeredElementChangeByPlayer(x, y, element,
11797 CE_OTHER_IS_SWITCHING,
11798 player->index_bit, dig_side);
11800 CheckTriggeredElementChangeByPlayer(x, y, element,
11801 CE_OTHER_IS_SWITCHING,
11802 player->index_bit, dig_side);
11803 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11804 player->index_bit, dig_side);
11809 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11810 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11811 player->index_bit, dig_side);
11812 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11813 player->index_bit, dig_side);
11815 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11816 player->index_bit, dig_side);
11817 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11818 player->index_bit, dig_side);
11822 return MF_NO_ACTION;
11825 #if USE_NEW_PUSH_DELAY
11826 player->push_delay = -1;
11828 player->push_delay = 0;
11831 if (Feld[x][y] != element) /* really digged/collected something */
11832 player->is_collecting = !player->is_digging;
11837 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11839 int jx = player->jx, jy = player->jy;
11840 int x = jx + dx, y = jy + dy;
11841 int snap_direction = (dx == -1 ? MV_LEFT :
11842 dx == +1 ? MV_RIGHT :
11844 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11847 if (player->MovPos != 0)
11850 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11854 if (!player->active || !IN_LEV_FIELD(x, y))
11862 if (player->MovPos == 0)
11863 player->is_pushing = FALSE;
11865 player->is_snapping = FALSE;
11867 if (player->MovPos == 0)
11869 player->is_moving = FALSE;
11870 player->is_digging = FALSE;
11871 player->is_collecting = FALSE;
11877 if (player->is_snapping)
11880 player->MovDir = snap_direction;
11883 if (player->MovPos == 0)
11886 player->is_moving = FALSE;
11887 player->is_digging = FALSE;
11888 player->is_collecting = FALSE;
11891 player->is_dropping = FALSE;
11893 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11896 player->is_snapping = TRUE;
11899 if (player->MovPos == 0)
11902 player->is_moving = FALSE;
11903 player->is_digging = FALSE;
11904 player->is_collecting = FALSE;
11908 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11909 DrawLevelField(player->last_jx, player->last_jy);
11912 DrawLevelField(x, y);
11921 boolean DropElement(struct PlayerInfo *player)
11923 int old_element, new_element;
11924 int dropx = player->jx, dropy = player->jy;
11925 int drop_direction = player->MovDir;
11927 int drop_side = drop_direction;
11929 static int trigger_sides[4] =
11931 CH_SIDE_LEFT, /* dropping left */
11932 CH_SIDE_RIGHT, /* dropping right */
11933 CH_SIDE_TOP, /* dropping up */
11934 CH_SIDE_BOTTOM, /* dropping down */
11936 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11938 int drop_element = (player->inventory_size > 0 ?
11939 player->inventory_element[player->inventory_size - 1] :
11940 player->inventory_infinite_element != EL_UNDEFINED ?
11941 player->inventory_infinite_element :
11942 player->dynabombs_left > 0 ?
11943 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11946 if (IS_THROWABLE(drop_element))
11948 dropx += GET_DX_FROM_DIR(drop_direction);
11949 dropy += GET_DY_FROM_DIR(drop_direction);
11951 if (!IN_LEV_FIELD(dropx, dropy))
11955 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11956 new_element = drop_element; /* default: no change when dropping */
11958 /* check if player is active, not moving and ready to drop */
11959 if (!player->active || player->MovPos || player->drop_delay > 0)
11962 /* check if player has anything that can be dropped */
11964 if (new_element == EL_UNDEFINED)
11967 if (player->inventory_size == 0 &&
11968 player->inventory_infinite_element == EL_UNDEFINED &&
11969 player->dynabombs_left == 0)
11973 /* check if anything can be dropped at the current position */
11974 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11977 /* collected custom elements can only be dropped on empty fields */
11979 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11982 if (player->inventory_size > 0 &&
11983 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11984 && old_element != EL_EMPTY)
11988 if (old_element != EL_EMPTY)
11989 Back[dropx][dropy] = old_element; /* store old element on this field */
11991 ResetGfxAnimation(dropx, dropy);
11992 ResetRandomAnimationValue(dropx, dropy);
11994 if (player->inventory_size > 0 ||
11995 player->inventory_infinite_element != EL_UNDEFINED)
11997 if (player->inventory_size > 0)
11999 player->inventory_size--;
12002 new_element = player->inventory_element[player->inventory_size];
12005 DrawGameValue_Dynamite(local_player->inventory_size);
12007 if (new_element == EL_DYNAMITE)
12008 new_element = EL_DYNAMITE_ACTIVE;
12009 else if (new_element == EL_SP_DISK_RED)
12010 new_element = EL_SP_DISK_RED_ACTIVE;
12013 Feld[dropx][dropy] = new_element;
12015 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12016 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12017 el2img(Feld[dropx][dropy]), 0);
12019 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12022 /* needed if previous element just changed to "empty" in the last frame */
12023 Changed[dropx][dropy] = 0; /* allow another change */
12027 /* !!! TEST ONLY !!! */
12028 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12029 player->index_bit, drop_side);
12030 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12031 CE_OTHER_GETS_DROPPED,
12032 player->index_bit, drop_side);
12034 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12035 CE_OTHER_GETS_DROPPED,
12036 player->index_bit, drop_side);
12037 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12038 player->index_bit, drop_side);
12041 TestIfElementTouchesCustomElement(dropx, dropy);
12043 else /* player is dropping a dyna bomb */
12045 player->dynabombs_left--;
12048 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12051 Feld[dropx][dropy] = new_element;
12053 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12054 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12055 el2img(Feld[dropx][dropy]), 0);
12057 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12064 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12067 InitField_WithBug1(dropx, dropy, FALSE);
12069 InitField(dropx, dropy, FALSE);
12070 if (CAN_MOVE(Feld[dropx][dropy]))
12071 InitMovDir(dropx, dropy);
12075 new_element = Feld[dropx][dropy]; /* element might have changed */
12077 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12078 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12081 int move_stepsize = element_info[new_element].move_stepsize;
12083 int move_direction, nextx, nexty;
12085 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12086 MovDir[dropx][dropy] = drop_direction;
12088 move_direction = MovDir[dropx][dropy];
12089 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12090 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12093 Changed[dropx][dropy] = 0; /* allow another change */
12094 CheckCollision[dropx][dropy] = 2;
12097 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12100 WasJustMoving[dropx][dropy] = 3;
12103 InitMovingField(dropx, dropy, move_direction);
12104 ContinueMoving(dropx, dropy);
12109 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12112 Changed[dropx][dropy] = 0; /* allow another change */
12115 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12117 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12118 CE_HITTING_SOMETHING, move_direction);
12126 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12131 player->drop_delay = 8 + 8 + 8;
12135 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12140 player->is_dropping = TRUE;
12146 /* ------------------------------------------------------------------------- */
12147 /* game sound playing functions */
12148 /* ------------------------------------------------------------------------- */
12150 static int *loop_sound_frame = NULL;
12151 static int *loop_sound_volume = NULL;
12153 void InitPlayLevelSound()
12155 int num_sounds = getSoundListSize();
12157 checked_free(loop_sound_frame);
12158 checked_free(loop_sound_volume);
12160 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12161 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12164 static void PlayLevelSound(int x, int y, int nr)
12166 int sx = SCREENX(x), sy = SCREENY(y);
12167 int volume, stereo_position;
12168 int max_distance = 8;
12169 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12171 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12172 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12175 if (!IN_LEV_FIELD(x, y) ||
12176 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12177 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12180 volume = SOUND_MAX_VOLUME;
12182 if (!IN_SCR_FIELD(sx, sy))
12184 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12185 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12187 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12190 stereo_position = (SOUND_MAX_LEFT +
12191 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12192 (SCR_FIELDX + 2 * max_distance));
12194 if (IS_LOOP_SOUND(nr))
12196 /* This assures that quieter loop sounds do not overwrite louder ones,
12197 while restarting sound volume comparison with each new game frame. */
12199 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12202 loop_sound_volume[nr] = volume;
12203 loop_sound_frame[nr] = FrameCounter;
12206 PlaySoundExt(nr, volume, stereo_position, type);
12209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12211 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12212 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12213 y < LEVELY(BY1) ? LEVELY(BY1) :
12214 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12218 static void PlayLevelSoundAction(int x, int y, int action)
12220 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12225 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12227 if (sound_effect != SND_UNDEFINED)
12228 PlayLevelSound(x, y, sound_effect);
12231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12234 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12236 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12237 PlayLevelSound(x, y, sound_effect);
12240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12242 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12244 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12245 PlayLevelSound(x, y, sound_effect);
12248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12250 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12252 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12253 StopSound(sound_effect);
12256 static void PlayLevelMusic()
12258 if (levelset.music[level_nr] != MUS_UNDEFINED)
12259 PlayMusic(levelset.music[level_nr]); /* from config file */
12261 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12264 void RaiseScore(int value)
12266 local_player->score += value;
12268 DrawGameValue_Score(local_player->score);
12271 void RaiseScoreElement(int element)
12276 case EL_BD_DIAMOND:
12277 case EL_EMERALD_YELLOW:
12278 case EL_EMERALD_RED:
12279 case EL_EMERALD_PURPLE:
12280 case EL_SP_INFOTRON:
12281 RaiseScore(level.score[SC_EMERALD]);
12284 RaiseScore(level.score[SC_DIAMOND]);
12287 RaiseScore(level.score[SC_CRYSTAL]);
12290 RaiseScore(level.score[SC_PEARL]);
12293 case EL_BD_BUTTERFLY:
12294 case EL_SP_ELECTRON:
12295 RaiseScore(level.score[SC_BUG]);
12298 case EL_BD_FIREFLY:
12299 case EL_SP_SNIKSNAK:
12300 RaiseScore(level.score[SC_SPACESHIP]);
12303 case EL_DARK_YAMYAM:
12304 RaiseScore(level.score[SC_YAMYAM]);
12307 RaiseScore(level.score[SC_ROBOT]);
12310 RaiseScore(level.score[SC_PACMAN]);
12313 RaiseScore(level.score[SC_NUT]);
12316 case EL_SP_DISK_RED:
12317 case EL_DYNABOMB_INCREASE_NUMBER:
12318 case EL_DYNABOMB_INCREASE_SIZE:
12319 case EL_DYNABOMB_INCREASE_POWER:
12320 RaiseScore(level.score[SC_DYNAMITE]);
12322 case EL_SHIELD_NORMAL:
12323 case EL_SHIELD_DEADLY:
12324 RaiseScore(level.score[SC_SHIELD]);
12326 case EL_EXTRA_TIME:
12327 RaiseScore(level.score[SC_TIME_BONUS]);
12333 RaiseScore(level.score[SC_KEY]);
12336 RaiseScore(element_info[element].collect_score);
12341 void RequestQuitGame(boolean ask_if_really_quit)
12343 if (AllPlayersGone ||
12344 !ask_if_really_quit ||
12345 level_editor_test_game ||
12346 Request("Do you really want to quit the game ?",
12347 REQ_ASK | REQ_STAY_CLOSED))
12349 #if defined(NETWORK_AVALIABLE)
12350 if (options.network)
12351 SendToServer_StopPlaying();
12355 game_status = GAME_MODE_MAIN;
12363 if (tape.playing && tape.deactivate_display)
12364 TapeDeactivateDisplayOff(TRUE);
12367 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12370 if (tape.playing && tape.deactivate_display)
12371 TapeDeactivateDisplayOn();
12378 /* ---------- new game button stuff ---------------------------------------- */
12380 /* graphic position values for game buttons */
12381 #define GAME_BUTTON_XSIZE 30
12382 #define GAME_BUTTON_YSIZE 30
12383 #define GAME_BUTTON_XPOS 5
12384 #define GAME_BUTTON_YPOS 215
12385 #define SOUND_BUTTON_XPOS 5
12386 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12388 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12389 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12390 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12391 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12392 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12393 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12400 } gamebutton_info[NUM_GAME_BUTTONS] =
12403 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12408 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12409 GAME_CTRL_ID_PAUSE,
12413 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12418 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12419 SOUND_CTRL_ID_MUSIC,
12420 "background music on/off"
12423 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12424 SOUND_CTRL_ID_LOOPS,
12425 "sound loops on/off"
12428 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12429 SOUND_CTRL_ID_SIMPLE,
12430 "normal sounds on/off"
12434 void CreateGameButtons()
12438 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12440 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12441 struct GadgetInfo *gi;
12444 unsigned long event_mask;
12445 int gd_xoffset, gd_yoffset;
12446 int gd_x1, gd_x2, gd_y1, gd_y2;
12449 gd_xoffset = gamebutton_info[i].x;
12450 gd_yoffset = gamebutton_info[i].y;
12451 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12452 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12454 if (id == GAME_CTRL_ID_STOP ||
12455 id == GAME_CTRL_ID_PAUSE ||
12456 id == GAME_CTRL_ID_PLAY)
12458 button_type = GD_TYPE_NORMAL_BUTTON;
12460 event_mask = GD_EVENT_RELEASED;
12461 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12462 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12466 button_type = GD_TYPE_CHECK_BUTTON;
12468 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12469 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12470 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12471 event_mask = GD_EVENT_PRESSED;
12472 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12473 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12476 gi = CreateGadget(GDI_CUSTOM_ID, id,
12477 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12478 GDI_X, DX + gd_xoffset,
12479 GDI_Y, DY + gd_yoffset,
12480 GDI_WIDTH, GAME_BUTTON_XSIZE,
12481 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12482 GDI_TYPE, button_type,
12483 GDI_STATE, GD_BUTTON_UNPRESSED,
12484 GDI_CHECKED, checked,
12485 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12486 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12487 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12488 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12489 GDI_EVENT_MASK, event_mask,
12490 GDI_CALLBACK_ACTION, HandleGameButtons,
12494 Error(ERR_EXIT, "cannot create gadget");
12496 game_gadget[id] = gi;
12500 void FreeGameButtons()
12504 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12505 FreeGadget(game_gadget[i]);
12508 static void MapGameButtons()
12512 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12513 MapGadget(game_gadget[i]);
12516 void UnmapGameButtons()
12520 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12521 UnmapGadget(game_gadget[i]);
12524 static void HandleGameButtons(struct GadgetInfo *gi)
12526 int id = gi->custom_id;
12528 if (game_status != GAME_MODE_PLAYING)
12533 case GAME_CTRL_ID_STOP:
12534 RequestQuitGame(TRUE);
12537 case GAME_CTRL_ID_PAUSE:
12538 if (options.network)
12540 #if defined(NETWORK_AVALIABLE)
12542 SendToServer_ContinuePlaying();
12544 SendToServer_PausePlaying();
12548 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12551 case GAME_CTRL_ID_PLAY:
12554 #if defined(NETWORK_AVALIABLE)
12555 if (options.network)
12556 SendToServer_ContinuePlaying();
12560 tape.pausing = FALSE;
12561 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12566 case SOUND_CTRL_ID_MUSIC:
12567 if (setup.sound_music)
12569 setup.sound_music = FALSE;
12572 else if (audio.music_available)
12574 setup.sound = setup.sound_music = TRUE;
12576 SetAudioMode(setup.sound);
12582 case SOUND_CTRL_ID_LOOPS:
12583 if (setup.sound_loops)
12584 setup.sound_loops = FALSE;
12585 else if (audio.loops_available)
12587 setup.sound = setup.sound_loops = TRUE;
12588 SetAudioMode(setup.sound);
12592 case SOUND_CTRL_ID_SIMPLE:
12593 if (setup.sound_simple)
12594 setup.sound_simple = FALSE;
12595 else if (audio.sound_available)
12597 setup.sound = setup.sound_simple = TRUE;
12598 SetAudioMode(setup.sound);