1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF (TRUE * 1)
33 #define USE_NEW_MOVE_STYLE (TRUE * USE_NEW_STUFF * 1)
34 #define USE_NEW_MOVE_DELAY (TRUE * USE_NEW_STUFF * 1)
35 #define USE_NEW_PUSH_DELAY (TRUE * USE_NEW_STUFF * 1)
36 #define USE_NEW_BLOCK_STYLE (TRUE * USE_NEW_STUFF * 1)
37 #define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
38 #define USE_NEW_RANDOMIZE (TRUE * USE_NEW_STUFF * 1)
40 #define USE_CAN_MOVE_NOT_MOVING (TRUE * USE_NEW_STUFF * 1)
41 #define USE_PREVIOUS_MOVE_DIR (TRUE * USE_NEW_STUFF * 1)
43 #define USE_PUSH_BUGFIX (TRUE * USE_NEW_STUFF * 1)
45 #define USE_BLOCK_DELAY_BUGFIX (TRUE * USE_NEW_STUFF * 1)
47 #define USE_GRAVITY_BUGFIX_NEW (TRUE * USE_NEW_STUFF * 1)
48 #define USE_GRAVITY_BUGFIX_OLD (TRUE * USE_NEW_STUFF * 0)
50 #define USE_PENGUIN_COLLECT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
52 #define USE_IMPACT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
54 #define USE_HITTING_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
55 #define USE_HIT_BY_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
57 #define USE_DROP_BUGFIX (TRUE * USE_NEW_STUFF * 1)
59 #define USE_CHANGE_TO_TRIGGERED (TRUE * USE_NEW_STUFF * 1)
61 #define USE_BACK_WALKABLE_BUGFIX (TRUE * USE_NEW_STUFF * 1)
63 #define TEST_NEW_STUFF (TRUE)
71 /* for MovePlayer() */
72 #define MF_NO_ACTION 0
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
89 /* special positions in the game control window (relative to control window) */
92 #define XX_EMERALDS 29
93 #define YY_EMERALDS 54
94 #define XX_DYNAMITE 29
95 #define YY_DYNAMITE 89
104 /* special positions in the game control window (relative to main window) */
105 #define DX_LEVEL (DX + XX_LEVEL)
106 #define DY_LEVEL (DY + YY_LEVEL)
107 #define DX_EMERALDS (DX + XX_EMERALDS)
108 #define DY_EMERALDS (DY + YY_EMERALDS)
109 #define DX_DYNAMITE (DX + XX_DYNAMITE)
110 #define DY_DYNAMITE (DY + YY_DYNAMITE)
111 #define DX_KEYS (DX + XX_KEYS)
112 #define DY_KEYS (DY + YY_KEYS)
113 #define DX_SCORE (DX + XX_SCORE)
114 #define DY_SCORE (DY + YY_SCORE)
115 #define DX_TIME1 (DX + XX_TIME1)
116 #define DX_TIME2 (DX + XX_TIME2)
117 #define DY_TIME (DY + YY_TIME)
119 /* values for initial player move delay (initial delay counter value) */
120 #define INITIAL_MOVE_DELAY_OFF -1
121 #define INITIAL_MOVE_DELAY_ON 0
123 /* values for player movement speed (which is in fact a delay value) */
124 #define MOVE_DELAY_NORMAL_SPEED 8
125 #define MOVE_DELAY_HIGH_SPEED 4
127 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
128 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
129 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
130 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
132 /* values for other actions */
133 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
135 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
136 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
138 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
140 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
141 RND(element_info[e].push_delay_random))
142 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
143 RND(element_info[e].drop_delay_random))
144 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
145 RND(element_info[e].move_delay_random))
146 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
147 (element_info[e].move_delay_random))
148 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
149 RND((c)->delay_random * (c)->delay_frames))
151 #define GET_TARGET_ELEMENT(e, ch) \
152 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
153 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
155 #define GET_VALID_PLAYER_ELEMENT(e) \
156 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
158 #define CAN_GROW_INTO(e) \
159 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
161 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
165 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
166 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
167 (CAN_MOVE_INTO_ACID(e) && \
168 Feld[x][y] == EL_ACID) || \
171 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
172 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
173 (CAN_MOVE_INTO_ACID(e) && \
174 Feld[x][y] == EL_ACID) || \
177 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
178 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
182 (DONT_COLLIDE_WITH(e) && \
184 !PLAYER_ENEMY_PROTECTED(x, y))))
187 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
188 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
190 (DONT_COLLIDE_WITH(e) && \
192 !PLAYER_ENEMY_PROTECTED(x, y))))
195 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
199 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
202 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
203 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
207 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
210 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
211 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
218 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
221 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
224 #define PIG_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
227 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
229 IS_FOOD_PENGUIN(Feld[x][y])))
230 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
236 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
241 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
242 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
243 (CAN_MOVE_INTO_ACID(e) && \
244 Feld[x][y] == EL_ACID) || \
245 Feld[x][y] == EL_DIAMOND))
247 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
248 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
249 (CAN_MOVE_INTO_ACID(e) && \
250 Feld[x][y] == EL_ACID) || \
251 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
253 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
254 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
255 (CAN_MOVE_INTO_ACID(e) && \
256 Feld[x][y] == EL_ACID) || \
257 IS_AMOEBOID(Feld[x][y])))
259 #define PIG_CAN_ENTER_FIELD(e, x, y) \
260 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
261 (CAN_MOVE_INTO_ACID(e) && \
262 Feld[x][y] == EL_ACID) || \
263 IS_FOOD_PIG(Feld[x][y])))
265 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
266 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
267 (CAN_MOVE_INTO_ACID(e) && \
268 Feld[x][y] == EL_ACID) || \
269 IS_FOOD_PENGUIN(Feld[x][y]) || \
270 Feld[x][y] == EL_EXIT_OPEN))
272 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
273 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
274 (CAN_MOVE_INTO_ACID(e) && \
275 Feld[x][y] == EL_ACID)))
277 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
278 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
279 (CAN_MOVE_INTO_ACID(e) && \
280 Feld[x][y] == EL_ACID) || \
283 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
284 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
285 (CAN_MOVE_INTO_ACID(e) && \
286 Feld[x][y] == EL_ACID)))
290 #define GROUP_NR(e) ((e) - EL_GROUP_START)
291 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
292 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
293 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
295 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
296 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
299 #define CE_ENTER_FIELD_COND(e, x, y) \
300 (!IS_PLAYER(x, y) && \
301 (Feld[x][y] == EL_ACID || \
302 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
304 #define CE_ENTER_FIELD_COND(e, x, y) \
305 (!IS_PLAYER(x, y) && \
306 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
309 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
310 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
312 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
313 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
315 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
316 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
317 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
318 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
320 /* game button identifiers */
321 #define GAME_CTRL_ID_STOP 0
322 #define GAME_CTRL_ID_PAUSE 1
323 #define GAME_CTRL_ID_PLAY 2
324 #define SOUND_CTRL_ID_MUSIC 3
325 #define SOUND_CTRL_ID_LOOPS 4
326 #define SOUND_CTRL_ID_SIMPLE 5
328 #define NUM_GAME_BUTTONS 6
331 /* forward declaration for internal use */
333 static void AdvanceFrameAndPlayerCounters(int);
335 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
336 static boolean MovePlayer(struct PlayerInfo *, int, int);
337 static void ScrollPlayer(struct PlayerInfo *, int);
338 static void ScrollScreen(struct PlayerInfo *, int);
340 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
342 static void InitBeltMovement(void);
343 static void CloseAllOpenTimegates(void);
344 static void CheckGravityMovement(struct PlayerInfo *);
345 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
346 static void KillHeroUnlessEnemyProtected(int, int);
347 static void KillHeroUnlessExplosionProtected(int, int);
349 static void TestIfPlayerTouchesCustomElement(int, int);
350 static void TestIfElementTouchesCustomElement(int, int);
351 static void TestIfElementHitsCustomElement(int, int, int);
353 static void TestIfElementSmashesCustomElement(int, int, int);
356 static void ChangeElement(int, int, int);
358 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
359 #define CheckTriggeredElementChange(x, y, e, ev) \
360 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
362 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
363 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
364 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
365 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
366 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
367 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
370 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
371 #define CheckElementChange(x, y, e, te, ev) \
372 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
373 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
374 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, -1)
375 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
376 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, -1)
377 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
378 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
380 static void PlayLevelSound(int, int, int);
381 static void PlayLevelSoundNearest(int, int, int);
382 static void PlayLevelSoundAction(int, int, int);
383 static void PlayLevelSoundElementAction(int, int, int, int);
384 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
385 static void PlayLevelSoundActionIfLoop(int, int, int);
386 static void StopLevelSoundActionIfLoop(int, int, int);
387 static void PlayLevelMusic();
389 static void MapGameButtons();
390 static void HandleGameButtons(struct GadgetInfo *);
392 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
395 /* ------------------------------------------------------------------------- */
396 /* definition of elements that automatically change to other elements after */
397 /* a specified time, eventually calling a function when changing */
398 /* ------------------------------------------------------------------------- */
400 /* forward declaration for changer functions */
401 static void InitBuggyBase(int x, int y);
402 static void WarnBuggyBase(int x, int y);
404 static void InitTrap(int x, int y);
405 static void ActivateTrap(int x, int y);
406 static void ChangeActiveTrap(int x, int y);
408 static void InitRobotWheel(int x, int y);
409 static void RunRobotWheel(int x, int y);
410 static void StopRobotWheel(int x, int y);
412 static void InitTimegateWheel(int x, int y);
413 static void RunTimegateWheel(int x, int y);
415 struct ChangingElementInfo
420 void (*pre_change_function)(int x, int y);
421 void (*change_function)(int x, int y);
422 void (*post_change_function)(int x, int y);
425 static struct ChangingElementInfo change_delay_list[] =
476 EL_SWITCHGATE_OPENING,
484 EL_SWITCHGATE_CLOSING,
485 EL_SWITCHGATE_CLOSED,
517 EL_ACID_SPLASH_RIGHT,
526 EL_SP_BUGGY_BASE_ACTIVATING,
533 EL_SP_BUGGY_BASE_ACTIVATING,
534 EL_SP_BUGGY_BASE_ACTIVE,
541 EL_SP_BUGGY_BASE_ACTIVE,
565 EL_ROBOT_WHEEL_ACTIVE,
573 EL_TIMEGATE_SWITCH_ACTIVE,
594 int push_delay_fixed, push_delay_random;
599 { EL_BALLOON, 0, 0 },
601 { EL_SOKOBAN_OBJECT, 2, 0 },
602 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
603 { EL_SATELLITE, 2, 0 },
604 { EL_SP_DISK_YELLOW, 2, 0 },
606 { EL_UNDEFINED, 0, 0 },
614 move_stepsize_list[] =
616 { EL_AMOEBA_DROP, 2 },
617 { EL_AMOEBA_DROPPING, 2 },
618 { EL_QUICKSAND_FILLING, 1 },
619 { EL_QUICKSAND_EMPTYING, 1 },
620 { EL_MAGIC_WALL_FILLING, 2 },
621 { EL_BD_MAGIC_WALL_FILLING, 2 },
622 { EL_MAGIC_WALL_EMPTYING, 2 },
623 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
633 collect_count_list[] =
636 { EL_BD_DIAMOND, 1 },
637 { EL_EMERALD_YELLOW, 1 },
638 { EL_EMERALD_RED, 1 },
639 { EL_EMERALD_PURPLE, 1 },
641 { EL_SP_INFOTRON, 1 },
653 access_direction_list[] =
655 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
656 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
657 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
658 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
659 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
660 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
661 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
662 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
663 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
664 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
665 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
667 { EL_SP_PORT_LEFT, MV_RIGHT },
668 { EL_SP_PORT_RIGHT, MV_LEFT },
669 { EL_SP_PORT_UP, MV_DOWN },
670 { EL_SP_PORT_DOWN, MV_UP },
671 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
672 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
673 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
674 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
675 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
676 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
677 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
678 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
679 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
680 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
681 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
682 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
683 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
684 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
685 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
687 { EL_UNDEFINED, MV_NO_MOVING }
690 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
692 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
693 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
694 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
695 IS_JUST_CHANGING(x, y))
697 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
700 void GetPlayerConfig()
702 if (!audio.sound_available)
703 setup.sound_simple = FALSE;
705 if (!audio.loops_available)
706 setup.sound_loops = FALSE;
708 if (!audio.music_available)
709 setup.sound_music = FALSE;
711 if (!video.fullscreen_available)
712 setup.fullscreen = FALSE;
714 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
716 SetAudioMode(setup.sound);
720 static int getBeltNrFromBeltElement(int element)
722 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
723 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
724 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
727 static int getBeltNrFromBeltActiveElement(int element)
729 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
730 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
731 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
734 static int getBeltNrFromBeltSwitchElement(int element)
736 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
737 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
738 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
741 static int getBeltDirNrFromBeltSwitchElement(int element)
743 static int belt_base_element[4] =
745 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
746 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
747 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
748 EL_CONVEYOR_BELT_4_SWITCH_LEFT
751 int belt_nr = getBeltNrFromBeltSwitchElement(element);
752 int belt_dir_nr = element - belt_base_element[belt_nr];
754 return (belt_dir_nr % 3);
757 static int getBeltDirFromBeltSwitchElement(int element)
759 static int belt_move_dir[3] =
766 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
768 return belt_move_dir[belt_dir_nr];
771 static void InitPlayerField(int x, int y, int element, boolean init_game)
773 if (element == EL_SP_MURPHY)
777 if (stored_player[0].present)
779 Feld[x][y] = EL_SP_MURPHY_CLONE;
785 stored_player[0].use_murphy_graphic = TRUE;
788 Feld[x][y] = EL_PLAYER_1;
794 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
795 int jx = player->jx, jy = player->jy;
797 player->present = TRUE;
799 player->block_last_field = (element == EL_SP_MURPHY ?
800 level.sp_block_last_field :
801 level.block_last_field);
803 #if USE_NEW_BLOCK_STYLE
806 /* ---------- initialize player's last field block delay --------------- */
808 /* always start with reliable default value (no adjustment needed) */
809 player->block_delay_adjustment = 0;
811 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
812 if (player->block_last_field && element == EL_SP_MURPHY)
813 player->block_delay_adjustment = 1;
815 /* special case 2: in game engines before 3.1.1, blocking was different */
816 if (game.use_block_last_field_bug)
817 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
820 /* blocking the last field when moving was corrected in version 3.1.1 */
821 if (game.use_block_last_field_bug)
823 /* even "not blocking" was blocking the last field for one frame */
824 level.block_delay = (level.block_last_field ? 7 : 1);
825 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
827 level.block_last_field = TRUE;
828 level.sp_block_last_field = TRUE;
832 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
833 level.block_delay = 8; /* when blocking, block 8 frames */
834 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
838 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
844 player->block_delay = (player->block_last_field ?
845 (element == EL_SP_MURPHY ?
846 level.sp_block_delay :
847 level.block_delay) : 0);
849 player->block_delay = (element == EL_SP_MURPHY ?
850 (player->block_last_field ? 7 : 1) :
851 (player->block_last_field ? 7 : 1));
857 printf("::: block_last_field == %d, block_delay = %d\n",
858 player->block_last_field, player->block_delay);
862 if (!options.network || player->connected)
864 player->active = TRUE;
866 /* remove potentially duplicate players */
867 if (StorePlayer[jx][jy] == Feld[x][y])
868 StorePlayer[jx][jy] = 0;
870 StorePlayer[x][y] = Feld[x][y];
874 printf("Player %d activated.\n", player->element_nr);
875 printf("[Local player is %d and currently %s.]\n",
876 local_player->element_nr,
877 local_player->active ? "active" : "not active");
881 Feld[x][y] = EL_EMPTY;
883 player->jx = player->last_jx = x;
884 player->jy = player->last_jy = y;
888 static void InitField(int x, int y, boolean init_game)
890 int element = Feld[x][y];
899 InitPlayerField(x, y, element, init_game);
902 case EL_SOKOBAN_FIELD_PLAYER:
903 element = Feld[x][y] = EL_PLAYER_1;
904 InitField(x, y, init_game);
906 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
907 InitField(x, y, init_game);
910 case EL_SOKOBAN_FIELD_EMPTY:
911 local_player->sokobanfields_still_needed++;
915 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
916 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
917 else if (x > 0 && Feld[x-1][y] == EL_ACID)
918 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
919 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
920 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
921 else if (y > 0 && Feld[x][y-1] == EL_ACID)
922 Feld[x][y] = EL_ACID_POOL_BOTTOM;
923 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
924 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
932 case EL_SPACESHIP_RIGHT:
933 case EL_SPACESHIP_UP:
934 case EL_SPACESHIP_LEFT:
935 case EL_SPACESHIP_DOWN:
937 case EL_BD_BUTTERFLY_RIGHT:
938 case EL_BD_BUTTERFLY_UP:
939 case EL_BD_BUTTERFLY_LEFT:
940 case EL_BD_BUTTERFLY_DOWN:
941 case EL_BD_BUTTERFLY:
942 case EL_BD_FIREFLY_RIGHT:
943 case EL_BD_FIREFLY_UP:
944 case EL_BD_FIREFLY_LEFT:
945 case EL_BD_FIREFLY_DOWN:
947 case EL_PACMAN_RIGHT:
971 if (y == lev_fieldy - 1)
973 Feld[x][y] = EL_AMOEBA_GROWING;
974 Store[x][y] = EL_AMOEBA_WET;
978 case EL_DYNAMITE_ACTIVE:
979 case EL_SP_DISK_RED_ACTIVE:
980 case EL_DYNABOMB_PLAYER_1_ACTIVE:
981 case EL_DYNABOMB_PLAYER_2_ACTIVE:
982 case EL_DYNABOMB_PLAYER_3_ACTIVE:
983 case EL_DYNABOMB_PLAYER_4_ACTIVE:
988 local_player->lights_still_needed++;
992 local_player->friends_still_needed++;
997 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1002 Feld[x][y] = EL_EMPTY;
1007 case EL_EM_KEY_1_FILE:
1008 Feld[x][y] = EL_EM_KEY_1;
1010 case EL_EM_KEY_2_FILE:
1011 Feld[x][y] = EL_EM_KEY_2;
1013 case EL_EM_KEY_3_FILE:
1014 Feld[x][y] = EL_EM_KEY_3;
1016 case EL_EM_KEY_4_FILE:
1017 Feld[x][y] = EL_EM_KEY_4;
1021 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1022 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1023 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1024 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1025 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1026 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1027 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1028 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1029 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1030 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1031 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1032 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1035 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1036 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1037 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1039 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1041 game.belt_dir[belt_nr] = belt_dir;
1042 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1044 else /* more than one switch -- set it like the first switch */
1046 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1051 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1053 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1056 case EL_LIGHT_SWITCH_ACTIVE:
1058 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1062 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1064 else if (IS_GROUP_ELEMENT(element))
1066 struct ElementGroupInfo *group = element_info[element].group;
1067 int last_anim_random_frame = gfx.anim_random_frame;
1070 if (group->choice_mode == ANIM_RANDOM)
1071 gfx.anim_random_frame = RND(group->num_elements_resolved);
1073 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1074 group->choice_mode, 0,
1077 if (group->choice_mode == ANIM_RANDOM)
1078 gfx.anim_random_frame = last_anim_random_frame;
1080 group->choice_pos++;
1082 Feld[x][y] = group->element_resolved[element_pos];
1084 InitField(x, y, init_game);
1090 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1092 InitField(x, y, init_game);
1094 /* not needed to call InitMovDir() -- already done by InitField()! */
1095 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1096 CAN_MOVE(Feld[x][y]))
1100 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1102 int old_element = Feld[x][y];
1104 InitField(x, y, init_game);
1106 /* not needed to call InitMovDir() -- already done by InitField()! */
1107 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1108 CAN_MOVE(old_element) &&
1109 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1112 /* this case is in fact a combination of not less than three bugs:
1113 first, it calls InitMovDir() for elements that can move, although this is
1114 already done by InitField(); then, it checks the element that was at this
1115 field _before_ the call to InitField() (which can change it); lastly, it
1116 was not called for "mole with direction" elements, which were treated as
1117 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1121 inline void DrawGameValue_Emeralds(int value)
1123 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1126 inline void DrawGameValue_Dynamite(int value)
1128 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1131 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1135 /* currently only 4 of 8 possible keys are displayed */
1136 for (i = 0; i < STD_NUM_KEYS; i++)
1139 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1140 el2edimg(EL_KEY_1 + i));
1142 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1143 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1144 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1148 inline void DrawGameValue_Score(int value)
1150 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1153 inline void DrawGameValue_Time(int value)
1156 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1158 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1161 inline void DrawGameValue_Level(int value)
1164 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1167 /* misuse area for displaying emeralds to draw bigger level number */
1168 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1169 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1171 /* now copy it to the area for displaying level number */
1172 BlitBitmap(drawto, drawto,
1173 DX_EMERALDS, DY_EMERALDS + 1,
1174 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1175 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1176 DX_LEVEL - 1, DY_LEVEL + 1);
1178 /* restore the area for displaying emeralds */
1179 DrawGameValue_Emeralds(local_player->gems_still_needed);
1181 /* yes, this is all really ugly :-) */
1185 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1188 int key[MAX_NUM_KEYS];
1191 for (i = 0; i < MAX_NUM_KEYS; i++)
1192 key[i] = key_bits & (1 << i);
1194 DrawGameValue_Level(level_nr);
1196 DrawGameValue_Emeralds(emeralds);
1197 DrawGameValue_Dynamite(dynamite);
1198 DrawGameValue_Score(score);
1199 DrawGameValue_Time(time);
1201 DrawGameValue_Keys(key);
1204 void DrawGameDoorValues()
1208 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1210 DrawGameDoorValues_EM();
1215 DrawGameValue_Level(level_nr);
1217 DrawGameValue_Emeralds(local_player->gems_still_needed);
1218 DrawGameValue_Dynamite(local_player->inventory_size);
1219 DrawGameValue_Score(local_player->score);
1220 DrawGameValue_Time(TimeLeft);
1222 for (i = 0; i < MAX_PLAYERS; i++)
1223 DrawGameValue_Keys(stored_player[i].key);
1226 static void resolve_group_element(int group_element, int recursion_depth)
1228 static int group_nr;
1229 static struct ElementGroupInfo *group;
1230 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1233 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1235 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1236 group_element - EL_GROUP_START + 1);
1238 /* replace element which caused too deep recursion by question mark */
1239 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1244 if (recursion_depth == 0) /* initialization */
1246 group = element_info[group_element].group;
1247 group_nr = group_element - EL_GROUP_START;
1249 group->num_elements_resolved = 0;
1250 group->choice_pos = 0;
1253 for (i = 0; i < actual_group->num_elements; i++)
1255 int element = actual_group->element[i];
1257 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1260 if (IS_GROUP_ELEMENT(element))
1261 resolve_group_element(element, recursion_depth + 1);
1264 group->element_resolved[group->num_elements_resolved++] = element;
1265 element_info[element].in_group[group_nr] = TRUE;
1270 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1272 printf("::: group %d: %d resolved elements\n",
1273 group_element - EL_GROUP_START, group->num_elements_resolved);
1274 for (i = 0; i < group->num_elements_resolved; i++)
1275 printf("::: - %d ['%s']\n", group->element_resolved[i],
1276 element_info[group->element_resolved[i]].token_name);
1283 =============================================================================
1285 -----------------------------------------------------------------------------
1286 initialize game engine due to level / tape version number
1287 =============================================================================
1290 static void InitGameEngine()
1294 /* set game engine from tape file when re-playing, else from level file */
1295 game.engine_version = (tape.playing ? tape.engine_version :
1296 level.game_version);
1298 /* ---------------------------------------------------------------------- */
1299 /* set flags for bugs and changes according to active game engine version */
1300 /* ---------------------------------------------------------------------- */
1303 Summary of bugfix/change:
1304 Fixed handling for custom elements that change when pushed by the player.
1306 Fixed/changed in version:
1310 Before 3.1.0, custom elements that "change when pushing" changed directly
1311 after the player started pushing them (until then handled in "DigField()").
1312 Since 3.1.0, these custom elements are not changed until the "pushing"
1313 move of the element is finished (now handled in "ContinueMoving()").
1315 Affected levels/tapes:
1316 The first condition is generally needed for all levels/tapes before version
1317 3.1.0, which might use the old behaviour before it was changed; known tapes
1318 that are affected are some tapes from the level set "Walpurgis Gardens" by
1320 The second condition is an exception from the above case and is needed for
1321 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1322 above (including some development versions of 3.1.0), but before it was
1323 known that this change would break tapes like the above and was fixed in
1324 3.1.1, so that the changed behaviour was active although the engine version
1325 while recording maybe was before 3.1.0. There is at least one tape that is
1326 affected by this exception, which is the tape for the one-level set "Bug
1327 Machine" by Juergen Bonhagen.
1330 game.use_change_when_pushing_bug =
1331 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1333 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1334 tape.game_version < VERSION_IDENT(3,1,1,0)));
1337 Summary of bugfix/change:
1338 Fixed handling for blocking the field the player leaves when moving.
1340 Fixed/changed in version:
1344 Before 3.1.1, when "block last field when moving" was enabled, the field
1345 the player is leaving when moving was blocked for the time of the move,
1346 and was directly unblocked afterwards. This resulted in the last field
1347 being blocked for exactly one less than the number of frames of one player
1348 move. Additionally, even when blocking was disabled, the last field was
1349 blocked for exactly one frame.
1350 Since 3.1.1, due to changes in player movement handling, the last field
1351 is not blocked at all when blocking is disabled. When blocking is enabled,
1352 the last field is blocked for exactly the number of frames of one player
1353 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1354 last field is blocked for exactly one more than the number of frames of
1357 Affected levels/tapes:
1358 (!!! yet to be determined -- probably many !!!)
1361 game.use_block_last_field_bug =
1362 (game.engine_version < VERSION_IDENT(3,1,1,0));
1364 /* ---------------------------------------------------------------------- */
1366 /* dynamically adjust element properties according to game engine version */
1367 InitElementPropertiesEngine(game.engine_version);
1370 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1371 printf(" tape version == %06d [%s] [file: %06d]\n",
1372 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1374 printf(" => game.engine_version == %06d\n", game.engine_version);
1377 /* ---------- recursively resolve group elements ------------------------- */
1379 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1380 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1381 element_info[i].in_group[j] = FALSE;
1383 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1384 resolve_group_element(EL_GROUP_START + i, 0);
1386 /* ---------- initialize player's initial move delay --------------------- */
1388 #if USE_NEW_MOVE_DELAY
1389 /* dynamically adjust player properties according to level information */
1390 game.initial_move_delay_value =
1391 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1393 /* dynamically adjust player properties according to game engine version */
1394 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1395 game.initial_move_delay_value : 0);
1397 /* dynamically adjust player properties according to game engine version */
1398 game.initial_move_delay =
1399 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1400 INITIAL_MOVE_DELAY_OFF);
1402 /* dynamically adjust player properties according to level information */
1403 game.initial_move_delay_value =
1404 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1407 /* ---------- initialize player's initial push delay --------------------- */
1409 /* dynamically adjust player properties according to game engine version */
1410 game.initial_push_delay_value =
1411 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1413 /* ---------- initialize changing elements ------------------------------- */
1415 /* initialize changing elements information */
1416 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1418 struct ElementInfo *ei = &element_info[i];
1420 /* this pointer might have been changed in the level editor */
1421 ei->change = &ei->change_page[0];
1423 if (!IS_CUSTOM_ELEMENT(i))
1425 ei->change->target_element = EL_EMPTY_SPACE;
1426 ei->change->delay_fixed = 0;
1427 ei->change->delay_random = 0;
1428 ei->change->delay_frames = 1;
1431 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1433 ei->has_change_event[j] = FALSE;
1435 ei->event_page_nr[j] = 0;
1436 ei->event_page[j] = &ei->change_page[0];
1440 /* add changing elements from pre-defined list */
1441 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1443 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1444 struct ElementInfo *ei = &element_info[ch_delay->element];
1446 ei->change->target_element = ch_delay->target_element;
1447 ei->change->delay_fixed = ch_delay->change_delay;
1449 ei->change->pre_change_function = ch_delay->pre_change_function;
1450 ei->change->change_function = ch_delay->change_function;
1451 ei->change->post_change_function = ch_delay->post_change_function;
1453 ei->has_change_event[CE_DELAY] = TRUE;
1456 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1461 /* add change events from custom element configuration */
1462 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1464 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1466 for (j = 0; j < ei->num_change_pages; j++)
1468 if (!ei->change_page[j].can_change)
1471 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1473 /* only add event page for the first page found with this event */
1474 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1476 ei->has_change_event[k] = TRUE;
1478 ei->event_page_nr[k] = j;
1479 ei->event_page[k] = &ei->change_page[j];
1487 /* add change events from custom element configuration */
1488 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1490 int element = EL_CUSTOM_START + i;
1492 /* only add custom elements that change after fixed/random frame delay */
1493 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1494 element_info[element].has_change_event[CE_DELAY] = TRUE;
1498 /* ---------- initialize internal run-time variables ------------- */
1500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1502 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1504 for (j = 0; j < ei->num_change_pages; j++)
1506 ei->change_page[j].can_change_or_has_action =
1507 (ei->change_page[j].can_change |
1508 ei->change_page[j].has_action);
1512 /* ---------- initialize run-time trigger player and element ------------- */
1514 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1516 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1518 for (j = 0; j < ei->num_change_pages; j++)
1520 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1521 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1525 /* ---------- initialize trigger events ---------------------------------- */
1527 /* initialize trigger events information */
1528 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1529 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1530 trigger_events[i][j] = FALSE;
1533 /* add trigger events from element change event properties */
1534 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1536 struct ElementInfo *ei = &element_info[i];
1538 for (j = 0; j < ei->num_change_pages; j++)
1540 if (!ei->change_page[j].can_change)
1543 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1545 int trigger_element = ei->change_page[j].trigger_element;
1547 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1549 if (ei->change_page[j].has_event[k])
1551 if (IS_GROUP_ELEMENT(trigger_element))
1553 struct ElementGroupInfo *group =
1554 element_info[trigger_element].group;
1556 for (l = 0; l < group->num_elements_resolved; l++)
1557 trigger_events[group->element_resolved[l]][k] = TRUE;
1560 trigger_events[trigger_element][k] = TRUE;
1567 /* add trigger events from element change event properties */
1568 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1569 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1570 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1571 if (element_info[i].change->has_event[j])
1572 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1575 /* ---------- initialize push delay -------------------------------------- */
1577 /* initialize push delay values to default */
1578 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1580 if (!IS_CUSTOM_ELEMENT(i))
1582 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1583 element_info[i].push_delay_random = game.default_push_delay_random;
1587 /* set push delay value for certain elements from pre-defined list */
1588 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1590 int e = push_delay_list[i].element;
1592 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1593 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1596 /* set push delay value for Supaplex elements for newer engine versions */
1597 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1599 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1601 if (IS_SP_ELEMENT(i))
1603 #if USE_NEW_MOVE_STYLE
1604 /* set SP push delay to just enough to push under a falling zonk */
1605 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1607 element_info[i].push_delay_fixed = delay;
1608 element_info[i].push_delay_random = 0;
1610 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1611 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1617 /* ---------- initialize move stepsize ----------------------------------- */
1619 /* initialize move stepsize values to default */
1620 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1621 if (!IS_CUSTOM_ELEMENT(i))
1622 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1624 /* set move stepsize value for certain elements from pre-defined list */
1625 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1627 int e = move_stepsize_list[i].element;
1629 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1633 /* ---------- initialize move dig/leave ---------------------------------- */
1635 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1637 element_info[i].can_leave_element = FALSE;
1638 element_info[i].can_leave_element_last = FALSE;
1642 /* ---------- initialize gem count --------------------------------------- */
1644 /* initialize gem count values for each element */
1645 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1646 if (!IS_CUSTOM_ELEMENT(i))
1647 element_info[i].collect_count = 0;
1649 /* add gem count values for all elements from pre-defined list */
1650 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1651 element_info[collect_count_list[i].element].collect_count =
1652 collect_count_list[i].count;
1654 /* ---------- initialize access direction -------------------------------- */
1656 /* initialize access direction values to default (access from every side) */
1657 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1658 if (!IS_CUSTOM_ELEMENT(i))
1659 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1661 /* set access direction value for certain elements from pre-defined list */
1662 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1663 element_info[access_direction_list[i].element].access_direction =
1664 access_direction_list[i].direction;
1669 =============================================================================
1671 -----------------------------------------------------------------------------
1672 initialize and start new game
1673 =============================================================================
1678 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1679 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1680 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1687 #if USE_NEW_AMOEBA_CODE
1688 printf("Using new amoeba code.\n");
1690 printf("Using old amoeba code.\n");
1695 /* don't play tapes over network */
1696 network_playing = (options.network && !tape.playing);
1698 for (i = 0; i < MAX_PLAYERS; i++)
1700 struct PlayerInfo *player = &stored_player[i];
1702 player->index_nr = i;
1703 player->index_bit = (1 << i);
1704 player->element_nr = EL_PLAYER_1 + i;
1706 player->present = FALSE;
1707 player->active = FALSE;
1710 player->effective_action = 0;
1711 player->programmed_action = 0;
1714 player->gems_still_needed = level.gems_needed;
1715 player->sokobanfields_still_needed = 0;
1716 player->lights_still_needed = 0;
1717 player->friends_still_needed = 0;
1719 for (j = 0; j < MAX_NUM_KEYS; j++)
1720 player->key[j] = FALSE;
1722 player->dynabomb_count = 0;
1723 player->dynabomb_size = 1;
1724 player->dynabombs_left = 0;
1725 player->dynabomb_xl = FALSE;
1727 player->MovDir = MV_NO_MOVING;
1730 player->GfxDir = MV_NO_MOVING;
1731 player->GfxAction = ACTION_DEFAULT;
1733 player->StepFrame = 0;
1735 player->use_murphy_graphic = FALSE;
1737 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1738 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1740 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1742 player->actual_frame_counter = 0;
1744 player->step_counter = 0;
1746 player->last_move_dir = MV_NO_MOVING;
1748 player->is_waiting = FALSE;
1749 player->is_moving = FALSE;
1750 player->is_auto_moving = FALSE;
1751 player->is_digging = FALSE;
1752 player->is_snapping = FALSE;
1753 player->is_collecting = FALSE;
1754 player->is_pushing = FALSE;
1755 player->is_switching = FALSE;
1756 player->is_dropping = FALSE;
1758 player->is_bored = FALSE;
1759 player->is_sleeping = FALSE;
1761 player->frame_counter_bored = -1;
1762 player->frame_counter_sleeping = -1;
1764 player->anim_delay_counter = 0;
1765 player->post_delay_counter = 0;
1767 player->action_waiting = ACTION_DEFAULT;
1768 player->last_action_waiting = ACTION_DEFAULT;
1769 player->special_action_bored = ACTION_DEFAULT;
1770 player->special_action_sleeping = ACTION_DEFAULT;
1772 player->num_special_action_bored = 0;
1773 player->num_special_action_sleeping = 0;
1775 /* determine number of special actions for bored and sleeping animation */
1776 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1778 boolean found = FALSE;
1780 for (k = 0; k < NUM_DIRECTIONS; k++)
1781 if (el_act_dir2img(player->element_nr, j, k) !=
1782 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1786 player->num_special_action_bored++;
1790 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1792 boolean found = FALSE;
1794 for (k = 0; k < NUM_DIRECTIONS; k++)
1795 if (el_act_dir2img(player->element_nr, j, k) !=
1796 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1800 player->num_special_action_sleeping++;
1805 player->switch_x = -1;
1806 player->switch_y = -1;
1809 player->drop_x = -1;
1810 player->drop_y = -1;
1813 player->show_envelope = 0;
1815 player->move_delay = game.initial_move_delay;
1816 player->move_delay_value = game.initial_move_delay_value;
1818 player->move_delay_reset_counter = 0;
1820 #if USE_NEW_PUSH_DELAY
1821 player->push_delay = -1; /* initialized when pushing starts */
1822 player->push_delay_value = game.initial_push_delay_value;
1824 player->push_delay = 0;
1825 player->push_delay_value = game.initial_push_delay_value;
1828 player->drop_delay = 0;
1830 player->last_jx = player->last_jy = 0;
1831 player->jx = player->jy = 0;
1833 player->shield_normal_time_left = 0;
1834 player->shield_deadly_time_left = 0;
1836 player->inventory_infinite_element = EL_UNDEFINED;
1837 player->inventory_size = 0;
1839 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1840 SnapField(player, 0, 0);
1842 player->LevelSolved = FALSE;
1843 player->GameOver = FALSE;
1846 network_player_action_received = FALSE;
1848 #if defined(NETWORK_AVALIABLE)
1849 /* initial null action */
1850 if (network_playing)
1851 SendToServer_MovePlayer(MV_NO_MOVING);
1860 TimeLeft = level.time;
1863 ScreenMovDir = MV_NO_MOVING;
1867 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1869 AllPlayersGone = FALSE;
1871 game.yamyam_content_nr = 0;
1872 game.magic_wall_active = FALSE;
1873 game.magic_wall_time_left = 0;
1874 game.light_time_left = 0;
1875 game.timegate_time_left = 0;
1876 game.switchgate_pos = 0;
1877 game.balloon_dir = MV_NO_MOVING;
1878 game.gravity = level.initial_gravity;
1879 game.explosions_delayed = TRUE;
1881 game.envelope_active = FALSE;
1883 for (i = 0; i < NUM_BELTS; i++)
1885 game.belt_dir[i] = MV_NO_MOVING;
1886 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1889 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1890 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1892 for (x = 0; x < lev_fieldx; x++)
1894 for (y = 0; y < lev_fieldy; y++)
1896 Feld[x][y] = level.field[x][y];
1897 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1898 ChangeDelay[x][y] = 0;
1899 ChangePage[x][y] = -1;
1900 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1902 WasJustMoving[x][y] = 0;
1903 WasJustFalling[x][y] = 0;
1904 CheckCollision[x][y] = 0;
1906 Pushed[x][y] = FALSE;
1908 Changed[x][y] = FALSE;
1909 ChangeEvent[x][y] = -1;
1911 ExplodePhase[x][y] = 0;
1912 ExplodeDelay[x][y] = 0;
1913 ExplodeField[x][y] = EX_TYPE_NONE;
1915 RunnerVisit[x][y] = 0;
1916 PlayerVisit[x][y] = 0;
1919 GfxRandom[x][y] = INIT_GFX_RANDOM();
1920 GfxElement[x][y] = EL_UNDEFINED;
1921 GfxAction[x][y] = ACTION_DEFAULT;
1922 GfxDir[x][y] = MV_NO_MOVING;
1926 for (y = 0; y < lev_fieldy; y++)
1928 for (x = 0; x < lev_fieldx; x++)
1930 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1932 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1934 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1937 InitField(x, y, TRUE);
1943 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1944 emulate_sb ? EMU_SOKOBAN :
1945 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1947 /* initialize explosion and ignition delay */
1948 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1950 if (!IS_CUSTOM_ELEMENT(i))
1953 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1954 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1955 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1956 int last_phase = (num_phase + 1) * delay;
1957 int half_phase = (num_phase / 2) * delay;
1959 element_info[i].explosion_delay = last_phase - 1;
1960 element_info[i].ignition_delay = half_phase;
1963 if (i == EL_BLACK_ORB)
1964 element_info[i].ignition_delay = 0;
1966 if (i == EL_BLACK_ORB)
1967 element_info[i].ignition_delay = 1;
1972 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1973 element_info[i].explosion_delay = 1;
1975 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1976 element_info[i].ignition_delay = 1;
1980 /* correct non-moving belts to start moving left */
1981 for (i = 0; i < NUM_BELTS; i++)
1982 if (game.belt_dir[i] == MV_NO_MOVING)
1983 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1985 /* check if any connected player was not found in playfield */
1986 for (i = 0; i < MAX_PLAYERS; i++)
1988 struct PlayerInfo *player = &stored_player[i];
1990 if (player->connected && !player->present)
1992 for (j = 0; j < MAX_PLAYERS; j++)
1994 struct PlayerInfo *some_player = &stored_player[j];
1995 int jx = some_player->jx, jy = some_player->jy;
1997 /* assign first free player found that is present in the playfield */
1998 if (some_player->present && !some_player->connected)
2000 player->present = TRUE;
2001 player->active = TRUE;
2003 some_player->present = FALSE;
2004 some_player->active = FALSE;
2007 player->element_nr = some_player->element_nr;
2010 #if USE_NEW_BLOCK_STYLE
2011 player->block_last_field = some_player->block_last_field;
2012 player->block_delay_adjustment = some_player->block_delay_adjustment;
2015 StorePlayer[jx][jy] = player->element_nr;
2016 player->jx = player->last_jx = jx;
2017 player->jy = player->last_jy = jy;
2027 /* when playing a tape, eliminate all players which do not participate */
2029 for (i = 0; i < MAX_PLAYERS; i++)
2031 if (stored_player[i].active && !tape.player_participates[i])
2033 struct PlayerInfo *player = &stored_player[i];
2034 int jx = player->jx, jy = player->jy;
2036 player->active = FALSE;
2037 StorePlayer[jx][jy] = 0;
2038 Feld[jx][jy] = EL_EMPTY;
2042 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2044 /* when in single player mode, eliminate all but the first active player */
2046 for (i = 0; i < MAX_PLAYERS; i++)
2048 if (stored_player[i].active)
2050 for (j = i + 1; j < MAX_PLAYERS; j++)
2052 if (stored_player[j].active)
2054 struct PlayerInfo *player = &stored_player[j];
2055 int jx = player->jx, jy = player->jy;
2057 player->active = FALSE;
2058 player->present = FALSE;
2060 StorePlayer[jx][jy] = 0;
2061 Feld[jx][jy] = EL_EMPTY;
2068 /* when recording the game, store which players take part in the game */
2071 for (i = 0; i < MAX_PLAYERS; i++)
2072 if (stored_player[i].active)
2073 tape.player_participates[i] = TRUE;
2078 for (i = 0; i < MAX_PLAYERS; i++)
2080 struct PlayerInfo *player = &stored_player[i];
2082 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2087 if (local_player == player)
2088 printf("Player %d is local player.\n", i+1);
2092 if (BorderElement == EL_EMPTY)
2095 SBX_Right = lev_fieldx - SCR_FIELDX;
2097 SBY_Lower = lev_fieldy - SCR_FIELDY;
2102 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2104 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2107 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2108 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2110 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2111 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2113 /* if local player not found, look for custom element that might create
2114 the player (make some assumptions about the right custom element) */
2115 if (!local_player->present)
2117 int start_x = 0, start_y = 0;
2118 int found_rating = 0;
2119 int found_element = EL_UNDEFINED;
2121 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2123 int element = Feld[x][y];
2128 if (!IS_CUSTOM_ELEMENT(element))
2131 if (CAN_CHANGE(element))
2133 for (i = 0; i < element_info[element].num_change_pages; i++)
2135 content = element_info[element].change_page[i].target_element;
2136 is_player = ELEM_IS_PLAYER(content);
2138 if (is_player && (found_rating < 3 || element < found_element))
2144 found_element = element;
2149 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2151 content = element_info[element].content[xx][yy];
2152 is_player = ELEM_IS_PLAYER(content);
2154 if (is_player && (found_rating < 2 || element < found_element))
2156 start_x = x + xx - 1;
2157 start_y = y + yy - 1;
2160 found_element = element;
2163 if (!CAN_CHANGE(element))
2166 for (i = 0; i < element_info[element].num_change_pages; i++)
2168 content= element_info[element].change_page[i].target_content[xx][yy];
2169 is_player = ELEM_IS_PLAYER(content);
2171 if (is_player && (found_rating < 1 || element < found_element))
2173 start_x = x + xx - 1;
2174 start_y = y + yy - 1;
2177 found_element = element;
2183 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2184 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2187 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2188 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2194 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2195 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2196 local_player->jx - MIDPOSX);
2198 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2199 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2200 local_player->jy - MIDPOSY);
2202 scroll_x = SBX_Left;
2203 scroll_y = SBY_Upper;
2204 if (local_player->jx >= SBX_Left + MIDPOSX)
2205 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2206 local_player->jx - MIDPOSX :
2208 if (local_player->jy >= SBY_Upper + MIDPOSY)
2209 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2210 local_player->jy - MIDPOSY :
2215 if (!game.restart_level)
2216 CloseDoor(DOOR_CLOSE_1);
2218 /* !!! FIX THIS (START) !!! */
2219 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2221 InitGameEngine_EM();
2228 /* after drawing the level, correct some elements */
2229 if (game.timegate_time_left == 0)
2230 CloseAllOpenTimegates();
2232 if (setup.soft_scrolling)
2233 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2235 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2238 /* !!! FIX THIS (END) !!! */
2240 if (!game.restart_level)
2242 /* copy default game door content to main double buffer */
2243 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2244 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2247 DrawGameDoorValues();
2249 if (!game.restart_level)
2253 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2254 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2255 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2259 /* copy actual game door content to door double buffer for OpenDoor() */
2260 BlitBitmap(drawto, bitmap_db_door,
2261 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2263 OpenDoor(DOOR_OPEN_ALL);
2265 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2267 if (setup.sound_music)
2270 KeyboardAutoRepeatOffUnlessAutoplay();
2274 for (i = 0; i < MAX_PLAYERS; i++)
2275 printf("Player %d %sactive.\n",
2276 i + 1, (stored_player[i].active ? "" : "not "));
2280 game.restart_level = FALSE;
2283 printf("::: starting game [%d]\n", FrameCounter);
2287 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2289 /* this is used for non-R'n'D game engines to update certain engine values */
2291 /* needed to determine if sounds are played within the visible screen area */
2292 scroll_x = actual_scroll_x;
2293 scroll_y = actual_scroll_y;
2296 void InitMovDir(int x, int y)
2298 int i, element = Feld[x][y];
2299 static int xy[4][2] =
2306 static int direction[3][4] =
2308 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2309 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2310 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2319 Feld[x][y] = EL_BUG;
2320 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2323 case EL_SPACESHIP_RIGHT:
2324 case EL_SPACESHIP_UP:
2325 case EL_SPACESHIP_LEFT:
2326 case EL_SPACESHIP_DOWN:
2327 Feld[x][y] = EL_SPACESHIP;
2328 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2331 case EL_BD_BUTTERFLY_RIGHT:
2332 case EL_BD_BUTTERFLY_UP:
2333 case EL_BD_BUTTERFLY_LEFT:
2334 case EL_BD_BUTTERFLY_DOWN:
2335 Feld[x][y] = EL_BD_BUTTERFLY;
2336 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2339 case EL_BD_FIREFLY_RIGHT:
2340 case EL_BD_FIREFLY_UP:
2341 case EL_BD_FIREFLY_LEFT:
2342 case EL_BD_FIREFLY_DOWN:
2343 Feld[x][y] = EL_BD_FIREFLY;
2344 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2347 case EL_PACMAN_RIGHT:
2349 case EL_PACMAN_LEFT:
2350 case EL_PACMAN_DOWN:
2351 Feld[x][y] = EL_PACMAN;
2352 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2355 case EL_SP_SNIKSNAK:
2356 MovDir[x][y] = MV_UP;
2359 case EL_SP_ELECTRON:
2360 MovDir[x][y] = MV_LEFT;
2367 Feld[x][y] = EL_MOLE;
2368 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2372 if (IS_CUSTOM_ELEMENT(element))
2374 struct ElementInfo *ei = &element_info[element];
2375 int move_direction_initial = ei->move_direction_initial;
2376 int move_pattern = ei->move_pattern;
2378 if (move_direction_initial == MV_START_PREVIOUS)
2380 if (MovDir[x][y] != MV_NO_MOVING)
2383 move_direction_initial = MV_START_AUTOMATIC;
2386 if (move_direction_initial == MV_START_RANDOM)
2387 MovDir[x][y] = 1 << RND(4);
2388 else if (move_direction_initial & MV_ANY_DIRECTION)
2389 MovDir[x][y] = move_direction_initial;
2390 else if (move_pattern == MV_ALL_DIRECTIONS ||
2391 move_pattern == MV_TURNING_LEFT ||
2392 move_pattern == MV_TURNING_RIGHT ||
2393 move_pattern == MV_TURNING_LEFT_RIGHT ||
2394 move_pattern == MV_TURNING_RIGHT_LEFT ||
2395 move_pattern == MV_TURNING_RANDOM)
2396 MovDir[x][y] = 1 << RND(4);
2397 else if (move_pattern == MV_HORIZONTAL)
2398 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2399 else if (move_pattern == MV_VERTICAL)
2400 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2401 else if (move_pattern & MV_ANY_DIRECTION)
2402 MovDir[x][y] = element_info[element].move_pattern;
2403 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2404 move_pattern == MV_ALONG_RIGHT_SIDE)
2407 /* use random direction as default start direction */
2408 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2409 MovDir[x][y] = 1 << RND(4);
2412 for (i = 0; i < NUM_DIRECTIONS; i++)
2414 int x1 = x + xy[i][0];
2415 int y1 = y + xy[i][1];
2417 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2419 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2420 MovDir[x][y] = direction[0][i];
2422 MovDir[x][y] = direction[1][i];
2431 MovDir[x][y] = 1 << RND(4);
2433 if (element != EL_BUG &&
2434 element != EL_SPACESHIP &&
2435 element != EL_BD_BUTTERFLY &&
2436 element != EL_BD_FIREFLY)
2439 for (i = 0; i < NUM_DIRECTIONS; i++)
2441 int x1 = x + xy[i][0];
2442 int y1 = y + xy[i][1];
2444 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2446 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2448 MovDir[x][y] = direction[0][i];
2451 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2452 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2454 MovDir[x][y] = direction[1][i];
2463 GfxDir[x][y] = MovDir[x][y];
2466 void InitAmoebaNr(int x, int y)
2469 int group_nr = AmoebeNachbarNr(x, y);
2473 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2475 if (AmoebaCnt[i] == 0)
2483 AmoebaNr[x][y] = group_nr;
2484 AmoebaCnt[group_nr]++;
2485 AmoebaCnt2[group_nr]++;
2491 boolean raise_level = FALSE;
2493 if (local_player->MovPos)
2497 if (tape.auto_play) /* tape might already be stopped here */
2498 tape.auto_play_level_solved = TRUE;
2500 if (tape.playing && tape.auto_play)
2501 tape.auto_play_level_solved = TRUE;
2504 local_player->LevelSolved = FALSE;
2506 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2510 if (!tape.playing && setup.sound_loops)
2511 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2512 SND_CTRL_PLAY_LOOP);
2514 while (TimeLeft > 0)
2516 if (!tape.playing && !setup.sound_loops)
2517 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2518 if (TimeLeft > 0 && !(TimeLeft % 10))
2519 RaiseScore(level.score[SC_TIME_BONUS]);
2520 if (TimeLeft > 100 && !(TimeLeft % 10))
2525 DrawGameValue_Time(TimeLeft);
2533 if (!tape.playing && setup.sound_loops)
2534 StopSound(SND_GAME_LEVELTIME_BONUS);
2536 else if (level.time == 0) /* level without time limit */
2538 if (!tape.playing && setup.sound_loops)
2539 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2540 SND_CTRL_PLAY_LOOP);
2542 while (TimePlayed < 999)
2544 if (!tape.playing && !setup.sound_loops)
2545 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2546 if (TimePlayed < 999 && !(TimePlayed % 10))
2547 RaiseScore(level.score[SC_TIME_BONUS]);
2548 if (TimePlayed < 900 && !(TimePlayed % 10))
2553 DrawGameValue_Time(TimePlayed);
2561 if (!tape.playing && setup.sound_loops)
2562 StopSound(SND_GAME_LEVELTIME_BONUS);
2565 /* close exit door after last player */
2566 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2567 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2568 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2570 int element = Feld[ExitX][ExitY];
2572 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2573 EL_SP_EXIT_CLOSING);
2575 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2578 /* Hero disappears */
2579 if (ExitX >= 0 && ExitY >= 0)
2580 DrawLevelField(ExitX, ExitY);
2587 CloseDoor(DOOR_CLOSE_1);
2592 SaveTape(tape.level_nr); /* Ask to save tape */
2595 if (level_nr == leveldir_current->handicap_level)
2597 leveldir_current->handicap_level++;
2598 SaveLevelSetup_SeriesInfo();
2601 if (level_editor_test_game)
2602 local_player->score = -1; /* no highscore when playing from editor */
2603 else if (level_nr < leveldir_current->last_level)
2604 raise_level = TRUE; /* advance to next level */
2606 if ((hi_pos = NewHiScore()) >= 0)
2608 game_status = GAME_MODE_SCORES;
2609 DrawHallOfFame(hi_pos);
2618 game_status = GAME_MODE_MAIN;
2635 LoadScore(level_nr);
2637 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2638 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2641 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2643 if (local_player->score > highscore[k].Score)
2645 /* player has made it to the hall of fame */
2647 if (k < MAX_SCORE_ENTRIES - 1)
2649 int m = MAX_SCORE_ENTRIES - 1;
2652 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2653 if (!strcmp(setup.player_name, highscore[l].Name))
2655 if (m == k) /* player's new highscore overwrites his old one */
2659 for (l = m; l > k; l--)
2661 strcpy(highscore[l].Name, highscore[l - 1].Name);
2662 highscore[l].Score = highscore[l - 1].Score;
2669 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2670 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2671 highscore[k].Score = local_player->score;
2677 else if (!strncmp(setup.player_name, highscore[k].Name,
2678 MAX_PLAYER_NAME_LEN))
2679 break; /* player already there with a higher score */
2685 SaveScore(level_nr);
2690 inline static int getElementMoveStepsize(int x, int y)
2692 int element = Feld[x][y];
2693 int direction = MovDir[x][y];
2694 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2695 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2696 int horiz_move = (dx != 0);
2697 int sign = (horiz_move ? dx : dy);
2698 int step = sign * element_info[element].move_stepsize;
2700 /* special values for move stepsize for spring and things on conveyor belt */
2704 if (element == EL_SPRING)
2705 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2706 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2707 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2708 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2710 if (CAN_FALL(element) &&
2711 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2712 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2713 else if (element == EL_SPRING)
2714 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2721 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2723 if (player->GfxAction != action || player->GfxDir != dir)
2726 printf("Player frame reset! (%d => %d, %d => %d)\n",
2727 player->GfxAction, action, player->GfxDir, dir);
2730 player->GfxAction = action;
2731 player->GfxDir = dir;
2733 player->StepFrame = 0;
2737 static void ResetRandomAnimationValue(int x, int y)
2739 GfxRandom[x][y] = INIT_GFX_RANDOM();
2742 static void ResetGfxAnimation(int x, int y)
2745 GfxAction[x][y] = ACTION_DEFAULT;
2746 GfxDir[x][y] = MovDir[x][y];
2749 void InitMovingField(int x, int y, int direction)
2751 int element = Feld[x][y];
2752 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2753 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2757 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2758 ResetGfxAnimation(x, y);
2760 #if USE_CAN_MOVE_NOT_MOVING
2762 MovDir[x][y] = direction;
2763 GfxDir[x][y] = direction;
2764 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2765 ACTION_FALLING : ACTION_MOVING);
2767 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2769 if (Feld[newx][newy] == EL_EMPTY)
2770 Feld[newx][newy] = EL_BLOCKED;
2772 MovDir[newx][newy] = MovDir[x][y];
2773 GfxFrame[newx][newy] = GfxFrame[x][y];
2774 GfxRandom[newx][newy] = GfxRandom[x][y];
2775 GfxAction[newx][newy] = GfxAction[x][y];
2776 GfxDir[newx][newy] = GfxDir[x][y];
2781 MovDir[newx][newy] = MovDir[x][y] = direction;
2782 GfxDir[x][y] = direction;
2784 if (Feld[newx][newy] == EL_EMPTY)
2785 Feld[newx][newy] = EL_BLOCKED;
2787 if (direction == MV_DOWN && CAN_FALL(element))
2788 GfxAction[x][y] = ACTION_FALLING;
2790 GfxAction[x][y] = ACTION_MOVING;
2792 GfxFrame[newx][newy] = GfxFrame[x][y];
2793 GfxRandom[newx][newy] = GfxRandom[x][y];
2794 GfxAction[newx][newy] = GfxAction[x][y];
2795 GfxDir[newx][newy] = GfxDir[x][y];
2799 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2801 int direction = MovDir[x][y];
2802 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2803 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2809 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2811 int oldx = x, oldy = y;
2812 int direction = MovDir[x][y];
2814 if (direction == MV_LEFT)
2816 else if (direction == MV_RIGHT)
2818 else if (direction == MV_UP)
2820 else if (direction == MV_DOWN)
2823 *comes_from_x = oldx;
2824 *comes_from_y = oldy;
2827 int MovingOrBlocked2Element(int x, int y)
2829 int element = Feld[x][y];
2831 if (element == EL_BLOCKED)
2835 Blocked2Moving(x, y, &oldx, &oldy);
2836 return Feld[oldx][oldy];
2842 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2844 /* like MovingOrBlocked2Element(), but if element is moving
2845 and (x,y) is the field the moving element is just leaving,
2846 return EL_BLOCKED instead of the element value */
2847 int element = Feld[x][y];
2849 if (IS_MOVING(x, y))
2851 if (element == EL_BLOCKED)
2855 Blocked2Moving(x, y, &oldx, &oldy);
2856 return Feld[oldx][oldy];
2865 static void RemoveField(int x, int y)
2867 Feld[x][y] = EL_EMPTY;
2874 ChangeDelay[x][y] = 0;
2875 ChangePage[x][y] = -1;
2876 Pushed[x][y] = FALSE;
2879 ExplodeField[x][y] = EX_TYPE_NONE;
2882 GfxElement[x][y] = EL_UNDEFINED;
2883 GfxAction[x][y] = ACTION_DEFAULT;
2884 GfxDir[x][y] = MV_NO_MOVING;
2887 void RemoveMovingField(int x, int y)
2889 int oldx = x, oldy = y, newx = x, newy = y;
2890 int element = Feld[x][y];
2891 int next_element = EL_UNDEFINED;
2893 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2896 if (IS_MOVING(x, y))
2898 Moving2Blocked(x, y, &newx, &newy);
2900 if (Feld[newx][newy] != EL_BLOCKED)
2903 if (Feld[newx][newy] != EL_BLOCKED)
2905 /* element is moving, but target field is not free (blocked), but
2906 already occupied by something different (example: acid pool);
2907 in this case, only remove the moving field, but not the target */
2909 RemoveField(oldx, oldy);
2911 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2913 DrawLevelField(oldx, oldy);
2919 else if (element == EL_BLOCKED)
2921 Blocked2Moving(x, y, &oldx, &oldy);
2922 if (!IS_MOVING(oldx, oldy))
2926 if (element == EL_BLOCKED &&
2927 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2928 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2929 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2930 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2931 next_element = get_next_element(Feld[oldx][oldy]);
2933 RemoveField(oldx, oldy);
2934 RemoveField(newx, newy);
2936 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2938 if (next_element != EL_UNDEFINED)
2939 Feld[oldx][oldy] = next_element;
2941 DrawLevelField(oldx, oldy);
2942 DrawLevelField(newx, newy);
2945 void DrawDynamite(int x, int y)
2947 int sx = SCREENX(x), sy = SCREENY(y);
2948 int graphic = el2img(Feld[x][y]);
2951 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2954 if (IS_WALKABLE_INSIDE(Back[x][y]))
2958 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2959 else if (Store[x][y])
2960 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2962 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2965 if (Back[x][y] || Store[x][y])
2966 DrawGraphicThruMask(sx, sy, graphic, frame);
2968 DrawGraphic(sx, sy, graphic, frame);
2970 if (game.emulation == EMU_SUPAPLEX)
2971 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2972 else if (Store[x][y])
2973 DrawGraphicThruMask(sx, sy, graphic, frame);
2975 DrawGraphic(sx, sy, graphic, frame);
2979 void CheckDynamite(int x, int y)
2981 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2985 if (MovDelay[x][y] != 0)
2988 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2995 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2997 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2998 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2999 StopSound(SND_DYNAMITE_ACTIVE);
3001 StopSound(SND_DYNABOMB_ACTIVE);
3007 void DrawRelocatePlayer(struct PlayerInfo *player)
3009 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3010 boolean no_delay = (tape.warp_forward);
3011 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3012 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3013 int jx = player->jx;
3014 int jy = player->jy;
3016 if (level.instant_relocation)
3019 int offset = (setup.scroll_delay ? 3 : 0);
3021 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3023 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3024 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3025 local_player->jx - MIDPOSX);
3027 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3028 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3029 local_player->jy - MIDPOSY);
3033 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3034 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3035 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3037 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3038 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3039 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3041 /* don't scroll over playfield boundaries */
3042 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3043 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3045 /* don't scroll over playfield boundaries */
3046 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3047 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3050 scroll_x += (local_player->jx - old_jx);
3051 scroll_y += (local_player->jy - old_jy);
3053 /* don't scroll over playfield boundaries */
3054 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3055 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3057 /* don't scroll over playfield boundaries */
3058 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3059 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3062 RedrawPlayfield(TRUE, 0,0,0,0);
3068 int offset = (setup.scroll_delay ? 3 : 0);
3070 int scroll_xx = -999, scroll_yy = -999;
3072 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3074 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3077 int fx = FX, fy = FY;
3079 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3080 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3081 local_player->jx - MIDPOSX);
3083 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3084 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3085 local_player->jy - MIDPOSY);
3087 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3088 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3091 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3094 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3101 fx += dx * TILEX / 2;
3102 fy += dy * TILEY / 2;
3104 ScrollLevel(dx, dy);
3107 /* scroll in two steps of half tile size to make things smoother */
3108 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3110 Delay(wait_delay_value);
3112 /* scroll second step to align at full tile size */
3114 Delay(wait_delay_value);
3117 int scroll_xx = -999, scroll_yy = -999;
3119 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3121 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3124 int fx = FX, fy = FY;
3126 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3127 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3128 local_player->jx - MIDPOSX);
3130 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3131 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3132 local_player->jy - MIDPOSY);
3134 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3135 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3138 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3141 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3148 fx += dx * TILEX / 2;
3149 fy += dy * TILEY / 2;
3151 ScrollLevel(dx, dy);
3154 /* scroll in two steps of half tile size to make things smoother */
3155 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3157 Delay(wait_delay_value);
3159 /* scroll second step to align at full tile size */
3161 Delay(wait_delay_value);
3167 Delay(wait_delay_value);
3171 void RelocatePlayer(int jx, int jy, int el_player_raw)
3174 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3176 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3178 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3179 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3180 boolean no_delay = (tape.warp_forward);
3181 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3182 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3183 int old_jx = player->jx;
3184 int old_jy = player->jy;
3185 int old_element = Feld[old_jx][old_jy];
3186 int element = Feld[jx][jy];
3187 boolean player_relocated = (old_jx != jx || old_jy != jy);
3189 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3190 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3192 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3193 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3194 int leave_side_horiz = move_dir_horiz;
3195 int leave_side_vert = move_dir_vert;
3197 static int trigger_sides[4][2] =
3199 /* enter side leave side */
3200 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3201 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3202 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3203 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3205 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3206 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3207 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3208 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3210 int enter_side = enter_side_horiz | enter_side_vert;
3211 int leave_side = leave_side_horiz | leave_side_vert;
3213 if (player->GameOver) /* do not reanimate dead player */
3216 if (!player_relocated) /* no need to relocate the player */
3219 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3221 RemoveField(jx, jy); /* temporarily remove newly placed player */
3222 DrawLevelField(jx, jy);
3225 if (player->present)
3227 while (player->MovPos)
3229 ScrollPlayer(player, SCROLL_GO_ON);
3230 ScrollScreen(NULL, SCROLL_GO_ON);
3232 #if USE_NEW_MOVE_DELAY
3233 AdvanceFrameAndPlayerCounters(player->index_nr);
3241 Delay(wait_delay_value);
3244 DrawPlayer(player); /* needed here only to cleanup last field */
3245 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3247 player->is_moving = FALSE;
3251 if (IS_CUSTOM_ELEMENT(old_element))
3252 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3254 player->index_bit, leave_side);
3256 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3258 player->index_bit, leave_side);
3261 Feld[jx][jy] = el_player;
3262 InitPlayerField(jx, jy, el_player, TRUE);
3264 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3266 Feld[jx][jy] = element;
3267 InitField(jx, jy, FALSE);
3271 if (player == local_player) /* only visually relocate local player */
3272 DrawRelocatePlayer(player);
3276 TestIfHeroTouchesBadThing(jx, jy);
3277 TestIfPlayerTouchesCustomElement(jx, jy);
3281 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3286 /* needed to allow change of walkable custom element by entering player */
3287 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3288 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3290 /* needed to allow change of walkable custom element by entering player */
3291 Changed[jx][jy] = 0; /* allow another change */
3296 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3297 enter_side == MV_LEFT ? "left" :
3298 enter_side == MV_RIGHT ? "right" :
3299 enter_side == MV_UP ? "top" :
3300 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3304 if (IS_CUSTOM_ELEMENT(element))
3305 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3306 player->index_bit, enter_side);
3308 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3310 player->index_bit, enter_side);
3314 void Explode(int ex, int ey, int phase, int mode)
3321 /* !!! eliminate this variable !!! */
3322 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3327 int last_phase = num_phase * delay;
3328 int half_phase = (num_phase / 2) * delay;
3329 int first_phase_after_start = EX_PHASE_START + 1;
3333 if (game.explosions_delayed)
3335 ExplodeField[ex][ey] = mode;
3339 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3341 int center_element = Feld[ex][ey];
3344 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3348 /* --- This is only really needed (and now handled) in "Impact()". --- */
3349 /* do not explode moving elements that left the explode field in time */
3350 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3351 center_element == EL_EMPTY &&
3352 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3357 if (mode == EX_TYPE_NORMAL ||
3358 mode == EX_TYPE_CENTER ||
3359 mode == EX_TYPE_CROSS)
3360 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3362 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3363 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3366 /* remove things displayed in background while burning dynamite */
3367 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3370 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3372 /* put moving element to center field (and let it explode there) */
3373 center_element = MovingOrBlocked2Element(ex, ey);
3374 RemoveMovingField(ex, ey);
3375 Feld[ex][ey] = center_element;
3381 last_phase = element_info[center_element].explosion_delay + 1;
3383 last_phase = element_info[center_element].explosion_delay;
3387 printf("::: %d -> %d\n", center_element, last_phase);
3391 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3393 int xx = x - ex + 1;
3394 int yy = y - ey + 1;
3399 if (!IN_LEV_FIELD(x, y) ||
3400 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3401 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3404 if (!IN_LEV_FIELD(x, y) ||
3405 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3409 if (!IN_LEV_FIELD(x, y) ||
3410 ((mode != EX_TYPE_NORMAL ||
3411 center_element == EL_AMOEBA_TO_DIAMOND) &&
3412 (x != ex || y != ey)))
3416 element = Feld[x][y];
3418 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3420 element = MovingOrBlocked2Element(x, y);
3422 if (!IS_EXPLOSION_PROOF(element))
3423 RemoveMovingField(x, y);
3429 if (IS_EXPLOSION_PROOF(element))
3432 /* indestructible elements can only explode in center (but not flames) */
3434 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3435 mode == EX_TYPE_BORDER)) ||
3436 element == EL_FLAMES)
3439 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3440 element == EL_FLAMES)
3446 if ((IS_INDESTRUCTIBLE(element) &&
3447 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3448 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3449 element == EL_FLAMES)
3453 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3454 behaviour, for example when touching a yamyam that explodes to rocks
3455 with active deadly shield, a rock is created under the player !!! */
3456 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3458 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3459 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3460 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3462 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3465 if (IS_ACTIVE_BOMB(element))
3467 /* re-activate things under the bomb like gate or penguin */
3469 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3472 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3477 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3478 element_info[Feld[x][y]].token_name,
3479 Store[x][y], Store2[x][y]);
3486 /* save walkable background elements while explosion on same tile */
3488 if (IS_INDESTRUCTIBLE(element))
3489 Back[x][y] = element;
3493 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3494 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3495 Back[x][y] = element;
3497 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3498 (x != ex || y != ey))
3499 Back[x][y] = element;
3502 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3503 Back[x][y] = element;
3507 /* ignite explodable elements reached by other explosion */
3508 if (element == EL_EXPLOSION)
3509 element = Store2[x][y];
3512 if (AmoebaNr[x][y] &&
3513 (element == EL_AMOEBA_FULL ||
3514 element == EL_BD_AMOEBA ||
3515 element == EL_AMOEBA_GROWING))
3517 AmoebaCnt[AmoebaNr[x][y]]--;
3518 AmoebaCnt2[AmoebaNr[x][y]]--;
3524 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3526 switch(StorePlayer[ex][ey])
3529 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3532 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3535 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3539 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3544 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3545 Store[x][y] = EL_EMPTY;
3547 if (game.emulation == EMU_SUPAPLEX)
3548 Store[x][y] = EL_EMPTY;
3551 else if (center_element == EL_MOLE)
3552 Store[x][y] = EL_EMERALD_RED;
3553 else if (center_element == EL_PENGUIN)
3554 Store[x][y] = EL_EMERALD_PURPLE;
3555 else if (center_element == EL_BUG)
3556 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3557 else if (center_element == EL_BD_BUTTERFLY)
3558 Store[x][y] = EL_BD_DIAMOND;
3559 else if (center_element == EL_SP_ELECTRON)
3560 Store[x][y] = EL_SP_INFOTRON;
3561 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3562 Store[x][y] = level.amoeba_content;
3563 else if (center_element == EL_YAMYAM)
3564 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3565 else if (IS_CUSTOM_ELEMENT(center_element) &&
3566 element_info[center_element].content[xx][yy] != EL_EMPTY)
3567 Store[x][y] = element_info[center_element].content[xx][yy];
3568 else if (element == EL_WALL_EMERALD)
3569 Store[x][y] = EL_EMERALD;
3570 else if (element == EL_WALL_DIAMOND)
3571 Store[x][y] = EL_DIAMOND;
3572 else if (element == EL_WALL_BD_DIAMOND)
3573 Store[x][y] = EL_BD_DIAMOND;
3574 else if (element == EL_WALL_EMERALD_YELLOW)
3575 Store[x][y] = EL_EMERALD_YELLOW;
3576 else if (element == EL_WALL_EMERALD_RED)
3577 Store[x][y] = EL_EMERALD_RED;
3578 else if (element == EL_WALL_EMERALD_PURPLE)
3579 Store[x][y] = EL_EMERALD_PURPLE;
3580 else if (element == EL_WALL_PEARL)
3581 Store[x][y] = EL_PEARL;
3582 else if (element == EL_WALL_CRYSTAL)
3583 Store[x][y] = EL_CRYSTAL;
3584 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3585 Store[x][y] = element_info[element].content[1][1];
3587 Store[x][y] = EL_EMPTY;
3589 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3590 center_element == EL_AMOEBA_TO_DIAMOND)
3591 Store2[x][y] = element;
3594 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3595 element_info[Store2[x][y]].token_name);
3599 if (AmoebaNr[x][y] &&
3600 (element == EL_AMOEBA_FULL ||
3601 element == EL_BD_AMOEBA ||
3602 element == EL_AMOEBA_GROWING))
3604 AmoebaCnt[AmoebaNr[x][y]]--;
3605 AmoebaCnt2[AmoebaNr[x][y]]--;
3611 MovDir[x][y] = MovPos[x][y] = 0;
3612 GfxDir[x][y] = MovDir[x][y];
3617 Feld[x][y] = EL_EXPLOSION;
3619 GfxElement[x][y] = center_element;
3621 GfxElement[x][y] = EL_UNDEFINED;
3624 ExplodePhase[x][y] = 1;
3626 ExplodeDelay[x][y] = last_phase;
3631 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3633 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3640 if (center_element == EL_YAMYAM)
3641 game.yamyam_content_nr =
3642 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3645 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3646 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3660 GfxFrame[x][y] = 0; /* restart explosion animation */
3664 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3668 last_phase = ExplodeDelay[x][y];
3671 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3675 /* activate this even in non-DEBUG version until cause for crash in
3676 getGraphicAnimationFrame() (see below) is found and eliminated */
3680 if (GfxElement[x][y] == EL_UNDEFINED)
3683 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3684 printf("Explode(): This should never happen!\n");
3687 GfxElement[x][y] = EL_EMPTY;
3693 border_element = Store2[x][y];
3695 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3696 border_element = StorePlayer[x][y];
3698 if (IS_PLAYER(x, y))
3699 border_element = StorePlayer[x][y];
3703 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3704 element_info[border_element].token_name, Store2[x][y]);
3708 printf("::: phase == %d\n", phase);
3711 if (phase == element_info[border_element].ignition_delay ||
3712 phase == last_phase)
3714 boolean border_explosion = FALSE;
3718 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3719 !PLAYER_EXPLOSION_PROTECTED(x, y))
3721 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3724 if (IS_PLAYER(x, y))
3727 KillHeroUnlessExplosionProtected(x, y);
3728 border_explosion = TRUE;
3731 if (phase == last_phase)
3732 printf("::: IS_PLAYER\n");
3735 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3738 printf("::: %d,%d: %d %s\n", x, y, border_element,
3739 element_info[border_element].token_name);
3742 Feld[x][y] = Store2[x][y];
3745 border_explosion = TRUE;
3748 if (phase == last_phase)
3749 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3752 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3754 AmoebeUmwandeln(x, y);
3756 border_explosion = TRUE;
3759 if (phase == last_phase)
3760 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3761 element_info[border_element].explosion_delay,
3762 element_info[border_element].ignition_delay,
3768 /* if an element just explodes due to another explosion (chain-reaction),
3769 do not immediately end the new explosion when it was the last frame of
3770 the explosion (as it would be done in the following "if"-statement!) */
3771 if (border_explosion && phase == last_phase)
3778 if (phase == first_phase_after_start)
3780 int element = Store2[x][y];
3782 if (element == EL_BLACK_ORB)
3784 Feld[x][y] = Store2[x][y];
3789 else if (phase == half_phase)
3791 int element = Store2[x][y];
3793 if (IS_PLAYER(x, y))
3794 KillHeroUnlessExplosionProtected(x, y);
3795 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3797 Feld[x][y] = Store2[x][y];
3801 else if (element == EL_AMOEBA_TO_DIAMOND)
3802 AmoebeUmwandeln(x, y);
3806 if (phase == last_phase)
3811 printf("::: done: phase == %d\n", phase);
3815 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3818 element = Feld[x][y] = Store[x][y];
3819 Store[x][y] = Store2[x][y] = 0;
3820 GfxElement[x][y] = EL_UNDEFINED;
3822 /* player can escape from explosions and might therefore be still alive */
3823 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3824 element <= EL_PLAYER_IS_EXPLODING_4)
3825 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3827 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3828 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3829 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3832 /* restore probably existing indestructible background element */
3833 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3834 element = Feld[x][y] = Back[x][y];
3837 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3838 GfxDir[x][y] = MV_NO_MOVING;
3839 ChangeDelay[x][y] = 0;
3840 ChangePage[x][y] = -1;
3843 InitField_WithBug2(x, y, FALSE);
3845 InitField(x, y, FALSE);
3847 /* !!! not needed !!! */
3849 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3850 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3853 if (CAN_MOVE(element))
3858 DrawLevelField(x, y);
3860 TestIfElementTouchesCustomElement(x, y);
3862 if (GFX_CRUMBLED(element))
3863 DrawLevelFieldCrumbledSandNeighbours(x, y);
3865 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3866 StorePlayer[x][y] = 0;
3868 if (ELEM_IS_PLAYER(element))
3869 RelocatePlayer(x, y, element);
3872 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3874 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3878 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3880 int stored = Store[x][y];
3881 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3882 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3886 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3888 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3892 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3896 printf("::: %d / %d [%d - %d]\n",
3897 GfxFrame[x][y], phase - delay, phase, delay);
3901 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3902 element_info[GfxElement[x][y]].token_name,
3907 DrawLevelFieldCrumbledSand(x, y);
3909 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3911 DrawLevelElement(x, y, Back[x][y]);
3912 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3914 else if (IS_WALKABLE_UNDER(Back[x][y]))
3916 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3917 DrawLevelElementThruMask(x, y, Back[x][y]);
3919 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3920 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3924 void DynaExplode(int ex, int ey)
3927 int dynabomb_element = Feld[ex][ey];
3928 int dynabomb_size = 1;
3929 boolean dynabomb_xl = FALSE;
3930 struct PlayerInfo *player;
3931 static int xy[4][2] =
3939 if (IS_ACTIVE_BOMB(dynabomb_element))
3941 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3942 dynabomb_size = player->dynabomb_size;
3943 dynabomb_xl = player->dynabomb_xl;
3944 player->dynabombs_left++;
3947 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3949 for (i = 0; i < NUM_DIRECTIONS; i++)
3951 for (j = 1; j <= dynabomb_size; j++)
3953 int x = ex + j * xy[i][0];
3954 int y = ey + j * xy[i][1];
3957 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3960 element = Feld[x][y];
3962 /* do not restart explosions of fields with active bombs */
3963 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3966 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3970 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3971 !IS_DIGGABLE(element) && !dynabomb_xl)
3974 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3975 !CAN_GROW_INTO(element) && !dynabomb_xl)
3979 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3980 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3981 element != EL_SAND && !dynabomb_xl)
3988 void Bang(int x, int y)
3991 int element = MovingOrBlocked2Element(x, y);
3993 int element = Feld[x][y];
3997 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3999 if (IS_PLAYER(x, y))
4002 struct PlayerInfo *player = PLAYERINFO(x, y);
4004 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
4005 player->element_nr);
4010 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
4012 if (game.emulation == EMU_SUPAPLEX)
4013 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
4015 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
4020 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
4028 case EL_BD_BUTTERFLY:
4031 case EL_DARK_YAMYAM:
4035 RaiseScoreElement(element);
4036 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4038 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4039 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4040 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4041 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4042 case EL_DYNABOMB_INCREASE_NUMBER:
4043 case EL_DYNABOMB_INCREASE_SIZE:
4044 case EL_DYNABOMB_INCREASE_POWER:
4049 case EL_LAMP_ACTIVE:
4051 case EL_AMOEBA_TO_DIAMOND:
4053 if (IS_PLAYER(x, y))
4054 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4056 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4060 if (element_info[element].explosion_type == EXPLODES_CROSS)
4062 if (CAN_EXPLODE_CROSS(element))
4065 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4070 else if (element_info[element].explosion_type == EXPLODES_1X1)
4072 else if (CAN_EXPLODE_1X1(element))
4074 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4076 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4080 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4083 void SplashAcid(int x, int y)
4086 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4087 (!IN_LEV_FIELD(x - 1, y - 2) ||
4088 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4089 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4091 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4092 (!IN_LEV_FIELD(x + 1, y - 2) ||
4093 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4094 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4096 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4098 /* input: position of element entering acid (obsolete) */
4100 int element = Feld[x][y];
4102 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4105 if (element != EL_ACID_SPLASH_LEFT &&
4106 element != EL_ACID_SPLASH_RIGHT)
4108 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4110 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4111 (!IN_LEV_FIELD(x - 1, y - 1) ||
4112 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4113 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4115 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4116 (!IN_LEV_FIELD(x + 1, y - 1) ||
4117 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4118 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4123 static void InitBeltMovement()
4125 static int belt_base_element[4] =
4127 EL_CONVEYOR_BELT_1_LEFT,
4128 EL_CONVEYOR_BELT_2_LEFT,
4129 EL_CONVEYOR_BELT_3_LEFT,
4130 EL_CONVEYOR_BELT_4_LEFT
4132 static int belt_base_active_element[4] =
4134 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4135 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4136 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4137 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4142 /* set frame order for belt animation graphic according to belt direction */
4143 for (i = 0; i < NUM_BELTS; i++)
4147 for (j = 0; j < NUM_BELT_PARTS; j++)
4149 int element = belt_base_active_element[belt_nr] + j;
4150 int graphic = el2img(element);
4152 if (game.belt_dir[i] == MV_LEFT)
4153 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4155 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4159 for (y = 0; y < lev_fieldy; y++)
4161 for (x = 0; x < lev_fieldx; x++)
4163 int element = Feld[x][y];
4165 for (i = 0; i < NUM_BELTS; i++)
4167 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4169 int e_belt_nr = getBeltNrFromBeltElement(element);
4172 if (e_belt_nr == belt_nr)
4174 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4176 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4184 static void ToggleBeltSwitch(int x, int y)
4186 static int belt_base_element[4] =
4188 EL_CONVEYOR_BELT_1_LEFT,
4189 EL_CONVEYOR_BELT_2_LEFT,
4190 EL_CONVEYOR_BELT_3_LEFT,
4191 EL_CONVEYOR_BELT_4_LEFT
4193 static int belt_base_active_element[4] =
4195 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4196 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4197 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4198 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4200 static int belt_base_switch_element[4] =
4202 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4203 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4204 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4205 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4207 static int belt_move_dir[4] =
4215 int element = Feld[x][y];
4216 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4217 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4218 int belt_dir = belt_move_dir[belt_dir_nr];
4221 if (!IS_BELT_SWITCH(element))
4224 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4225 game.belt_dir[belt_nr] = belt_dir;
4227 if (belt_dir_nr == 3)
4230 /* set frame order for belt animation graphic according to belt direction */
4231 for (i = 0; i < NUM_BELT_PARTS; i++)
4233 int element = belt_base_active_element[belt_nr] + i;
4234 int graphic = el2img(element);
4236 if (belt_dir == MV_LEFT)
4237 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4239 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4242 for (yy = 0; yy < lev_fieldy; yy++)
4244 for (xx = 0; xx < lev_fieldx; xx++)
4246 int element = Feld[xx][yy];
4248 if (IS_BELT_SWITCH(element))
4250 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4252 if (e_belt_nr == belt_nr)
4254 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4255 DrawLevelField(xx, yy);
4258 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4260 int e_belt_nr = getBeltNrFromBeltElement(element);
4262 if (e_belt_nr == belt_nr)
4264 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4266 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4267 DrawLevelField(xx, yy);
4270 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4272 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4274 if (e_belt_nr == belt_nr)
4276 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4278 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4279 DrawLevelField(xx, yy);
4286 static void ToggleSwitchgateSwitch(int x, int y)
4290 game.switchgate_pos = !game.switchgate_pos;
4292 for (yy = 0; yy < lev_fieldy; yy++)
4294 for (xx = 0; xx < lev_fieldx; xx++)
4296 int element = Feld[xx][yy];
4298 if (element == EL_SWITCHGATE_SWITCH_UP ||
4299 element == EL_SWITCHGATE_SWITCH_DOWN)
4301 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4302 DrawLevelField(xx, yy);
4304 else if (element == EL_SWITCHGATE_OPEN ||
4305 element == EL_SWITCHGATE_OPENING)
4307 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4309 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4311 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4314 else if (element == EL_SWITCHGATE_CLOSED ||
4315 element == EL_SWITCHGATE_CLOSING)
4317 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4319 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4321 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4328 static int getInvisibleActiveFromInvisibleElement(int element)
4330 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4331 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4332 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4336 static int getInvisibleFromInvisibleActiveElement(int element)
4338 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4339 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4340 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4344 static void RedrawAllLightSwitchesAndInvisibleElements()
4348 for (y = 0; y < lev_fieldy; y++)
4350 for (x = 0; x < lev_fieldx; x++)
4352 int element = Feld[x][y];
4354 if (element == EL_LIGHT_SWITCH &&
4355 game.light_time_left > 0)
4357 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4358 DrawLevelField(x, y);
4360 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4361 game.light_time_left == 0)
4363 Feld[x][y] = EL_LIGHT_SWITCH;
4364 DrawLevelField(x, y);
4366 else if (element == EL_INVISIBLE_STEELWALL ||
4367 element == EL_INVISIBLE_WALL ||
4368 element == EL_INVISIBLE_SAND)
4370 if (game.light_time_left > 0)
4371 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4373 DrawLevelField(x, y);
4375 /* uncrumble neighbour fields, if needed */
4376 if (element == EL_INVISIBLE_SAND)
4377 DrawLevelFieldCrumbledSandNeighbours(x, y);
4379 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4380 element == EL_INVISIBLE_WALL_ACTIVE ||
4381 element == EL_INVISIBLE_SAND_ACTIVE)
4383 if (game.light_time_left == 0)
4384 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4386 DrawLevelField(x, y);
4388 /* re-crumble neighbour fields, if needed */
4389 if (element == EL_INVISIBLE_SAND)
4390 DrawLevelFieldCrumbledSandNeighbours(x, y);
4396 static void ToggleLightSwitch(int x, int y)
4398 int element = Feld[x][y];
4400 game.light_time_left =
4401 (element == EL_LIGHT_SWITCH ?
4402 level.time_light * FRAMES_PER_SECOND : 0);
4404 RedrawAllLightSwitchesAndInvisibleElements();
4407 static void ActivateTimegateSwitch(int x, int y)
4411 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4413 for (yy = 0; yy < lev_fieldy; yy++)
4415 for (xx = 0; xx < lev_fieldx; xx++)
4417 int element = Feld[xx][yy];
4419 if (element == EL_TIMEGATE_CLOSED ||
4420 element == EL_TIMEGATE_CLOSING)
4422 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4423 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4427 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4429 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4430 DrawLevelField(xx, yy);
4437 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4440 void Impact(int x, int y)
4442 boolean last_line = (y == lev_fieldy - 1);
4443 boolean object_hit = FALSE;
4444 boolean impact = (last_line || object_hit);
4445 int element = Feld[x][y];
4446 int smashed = EL_STEELWALL;
4449 printf("IMPACT!\n");
4452 if (!last_line) /* check if element below was hit */
4454 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4457 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4458 MovDir[x][y + 1] != MV_DOWN ||
4459 MovPos[x][y + 1] <= TILEY / 2));
4462 object_hit = !IS_FREE(x, y + 1);
4465 /* do not smash moving elements that left the smashed field in time */
4466 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4467 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4471 smashed = MovingOrBlocked2Element(x, y + 1);
4473 impact = (last_line || object_hit);
4476 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4478 SplashAcid(x, y + 1);
4482 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4483 /* only reset graphic animation if graphic really changes after impact */
4485 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4487 ResetGfxAnimation(x, y);
4488 DrawLevelField(x, y);
4491 if (impact && CAN_EXPLODE_IMPACT(element))
4496 else if (impact && element == EL_PEARL)
4498 ResetGfxAnimation(x, y);
4500 Feld[x][y] = EL_PEARL_BREAKING;
4501 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4504 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4506 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4511 if (impact && element == EL_AMOEBA_DROP)
4513 if (object_hit && IS_PLAYER(x, y + 1))
4514 KillHeroUnlessEnemyProtected(x, y + 1);
4515 else if (object_hit && smashed == EL_PENGUIN)
4519 Feld[x][y] = EL_AMOEBA_GROWING;
4520 Store[x][y] = EL_AMOEBA_WET;
4522 ResetRandomAnimationValue(x, y);
4527 if (object_hit) /* check which object was hit */
4529 if (CAN_PASS_MAGIC_WALL(element) &&
4530 (smashed == EL_MAGIC_WALL ||
4531 smashed == EL_BD_MAGIC_WALL))
4534 int activated_magic_wall =
4535 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4536 EL_BD_MAGIC_WALL_ACTIVE);
4538 /* activate magic wall / mill */
4539 for (yy = 0; yy < lev_fieldy; yy++)
4540 for (xx = 0; xx < lev_fieldx; xx++)
4541 if (Feld[xx][yy] == smashed)
4542 Feld[xx][yy] = activated_magic_wall;
4544 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4545 game.magic_wall_active = TRUE;
4547 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4548 SND_MAGIC_WALL_ACTIVATING :
4549 SND_BD_MAGIC_WALL_ACTIVATING));
4552 if (IS_PLAYER(x, y + 1))
4554 if (CAN_SMASH_PLAYER(element))
4556 KillHeroUnlessEnemyProtected(x, y + 1);
4560 else if (smashed == EL_PENGUIN)
4562 if (CAN_SMASH_PLAYER(element))
4568 else if (element == EL_BD_DIAMOND)
4570 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4576 else if (((element == EL_SP_INFOTRON ||
4577 element == EL_SP_ZONK) &&
4578 (smashed == EL_SP_SNIKSNAK ||
4579 smashed == EL_SP_ELECTRON ||
4580 smashed == EL_SP_DISK_ORANGE)) ||
4581 (element == EL_SP_INFOTRON &&
4582 smashed == EL_SP_DISK_YELLOW))
4588 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4594 else if (CAN_SMASH_EVERYTHING(element))
4596 if (IS_CLASSIC_ENEMY(smashed) ||
4597 CAN_EXPLODE_SMASHED(smashed))
4602 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4604 if (smashed == EL_LAMP ||
4605 smashed == EL_LAMP_ACTIVE)
4610 else if (smashed == EL_NUT)
4612 Feld[x][y + 1] = EL_NUT_BREAKING;
4613 PlayLevelSound(x, y, SND_NUT_BREAKING);
4614 RaiseScoreElement(EL_NUT);
4617 else if (smashed == EL_PEARL)
4619 ResetGfxAnimation(x, y);
4621 Feld[x][y + 1] = EL_PEARL_BREAKING;
4622 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4625 else if (smashed == EL_DIAMOND)
4627 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4628 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4631 else if (IS_BELT_SWITCH(smashed))
4633 ToggleBeltSwitch(x, y + 1);
4635 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4636 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4638 ToggleSwitchgateSwitch(x, y + 1);
4640 else if (smashed == EL_LIGHT_SWITCH ||
4641 smashed == EL_LIGHT_SWITCH_ACTIVE)
4643 ToggleLightSwitch(x, y + 1);
4648 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4651 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4654 /* !!! TEST ONLY !!! */
4655 CheckElementChangeBySide(x, y + 1, smashed, element,
4656 CE_SWITCHED, CH_SIDE_TOP);
4657 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4658 CE_SWITCH_OF_X, CH_SIDE_TOP);
4660 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4661 CE_SWITCH_OF_X, CH_SIDE_TOP);
4662 CheckElementChangeBySide(x, y + 1, smashed, element,
4663 CE_SWITCHED, CH_SIDE_TOP);
4669 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4674 /* play sound of magic wall / mill */
4676 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4677 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4679 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4680 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4681 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4682 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4687 /* play sound of object that hits the ground */
4688 if (last_line || object_hit)
4689 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4692 inline static void TurnRoundExt(int x, int y)
4704 { 0, 0 }, { 0, 0 }, { 0, 0 },
4709 int left, right, back;
4713 { MV_DOWN, MV_UP, MV_RIGHT },
4714 { MV_UP, MV_DOWN, MV_LEFT },
4716 { MV_LEFT, MV_RIGHT, MV_DOWN },
4720 { MV_RIGHT, MV_LEFT, MV_UP }
4723 int element = Feld[x][y];
4724 int move_pattern = element_info[element].move_pattern;
4726 int old_move_dir = MovDir[x][y];
4727 int left_dir = turn[old_move_dir].left;
4728 int right_dir = turn[old_move_dir].right;
4729 int back_dir = turn[old_move_dir].back;
4731 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4732 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4733 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4734 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4736 int left_x = x + left_dx, left_y = y + left_dy;
4737 int right_x = x + right_dx, right_y = y + right_dy;
4738 int move_x = x + move_dx, move_y = y + move_dy;
4742 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4744 TestIfBadThingTouchesOtherBadThing(x, y);
4746 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4747 MovDir[x][y] = right_dir;
4748 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4749 MovDir[x][y] = left_dir;
4751 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4753 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4757 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4758 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4760 TestIfBadThingTouchesOtherBadThing(x, y);
4762 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4763 MovDir[x][y] = left_dir;
4764 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4765 MovDir[x][y] = right_dir;
4767 if ((element == EL_SPACESHIP ||
4768 element == EL_SP_SNIKSNAK ||
4769 element == EL_SP_ELECTRON)
4770 && MovDir[x][y] != old_move_dir)
4772 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4776 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4778 TestIfBadThingTouchesOtherBadThing(x, y);
4780 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4781 MovDir[x][y] = left_dir;
4782 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4783 MovDir[x][y] = right_dir;
4785 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4787 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4790 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4792 TestIfBadThingTouchesOtherBadThing(x, y);
4794 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4795 MovDir[x][y] = left_dir;
4796 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4797 MovDir[x][y] = right_dir;
4799 if (MovDir[x][y] != old_move_dir)
4803 else if (element == EL_YAMYAM)
4805 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4806 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4808 if (can_turn_left && can_turn_right)
4809 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4810 else if (can_turn_left)
4811 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4812 else if (can_turn_right)
4813 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4815 MovDir[x][y] = back_dir;
4817 MovDelay[x][y] = 16 + 16 * RND(3);
4819 else if (element == EL_DARK_YAMYAM)
4821 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4823 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4826 if (can_turn_left && can_turn_right)
4827 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4828 else if (can_turn_left)
4829 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4830 else if (can_turn_right)
4831 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4833 MovDir[x][y] = back_dir;
4835 MovDelay[x][y] = 16 + 16 * RND(3);
4837 else if (element == EL_PACMAN)
4839 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4840 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4842 if (can_turn_left && can_turn_right)
4843 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4844 else if (can_turn_left)
4845 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4846 else if (can_turn_right)
4847 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4849 MovDir[x][y] = back_dir;
4851 MovDelay[x][y] = 6 + RND(40);
4853 else if (element == EL_PIG)
4855 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4856 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4857 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4858 boolean should_turn_left, should_turn_right, should_move_on;
4860 int rnd = RND(rnd_value);
4862 should_turn_left = (can_turn_left &&
4864 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4865 y + back_dy + left_dy)));
4866 should_turn_right = (can_turn_right &&
4868 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4869 y + back_dy + right_dy)));
4870 should_move_on = (can_move_on &&
4873 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4874 y + move_dy + left_dy) ||
4875 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4876 y + move_dy + right_dy)));
4878 if (should_turn_left || should_turn_right || should_move_on)
4880 if (should_turn_left && should_turn_right && should_move_on)
4881 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4882 rnd < 2 * rnd_value / 3 ? right_dir :
4884 else if (should_turn_left && should_turn_right)
4885 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4886 else if (should_turn_left && should_move_on)
4887 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4888 else if (should_turn_right && should_move_on)
4889 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4890 else if (should_turn_left)
4891 MovDir[x][y] = left_dir;
4892 else if (should_turn_right)
4893 MovDir[x][y] = right_dir;
4894 else if (should_move_on)
4895 MovDir[x][y] = old_move_dir;
4897 else if (can_move_on && rnd > rnd_value / 8)
4898 MovDir[x][y] = old_move_dir;
4899 else if (can_turn_left && can_turn_right)
4900 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4901 else if (can_turn_left && rnd > rnd_value / 8)
4902 MovDir[x][y] = left_dir;
4903 else if (can_turn_right && rnd > rnd_value/8)
4904 MovDir[x][y] = right_dir;
4906 MovDir[x][y] = back_dir;
4908 xx = x + move_xy[MovDir[x][y]].x;
4909 yy = y + move_xy[MovDir[x][y]].y;
4912 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4913 if (!IN_LEV_FIELD(xx, yy) ||
4914 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4915 MovDir[x][y] = old_move_dir;
4917 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4918 MovDir[x][y] = old_move_dir;
4923 else if (element == EL_DRAGON)
4925 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4926 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4927 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4929 int rnd = RND(rnd_value);
4932 if (FrameCounter < 1 && x == 0 && y == 29)
4933 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4936 if (can_move_on && rnd > rnd_value / 8)
4937 MovDir[x][y] = old_move_dir;
4938 else if (can_turn_left && can_turn_right)
4939 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4940 else if (can_turn_left && rnd > rnd_value / 8)
4941 MovDir[x][y] = left_dir;
4942 else if (can_turn_right && rnd > rnd_value / 8)
4943 MovDir[x][y] = right_dir;
4945 MovDir[x][y] = back_dir;
4947 xx = x + move_xy[MovDir[x][y]].x;
4948 yy = y + move_xy[MovDir[x][y]].y;
4951 if (FrameCounter < 1 && x == 0 && y == 29)
4952 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4953 xx, yy, Feld[xx][yy],
4958 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4959 MovDir[x][y] = old_move_dir;
4961 if (!IS_FREE(xx, yy))
4962 MovDir[x][y] = old_move_dir;
4966 if (FrameCounter < 1 && x == 0 && y == 29)
4967 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4972 else if (element == EL_MOLE)
4974 boolean can_move_on =
4975 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4976 IS_AMOEBOID(Feld[move_x][move_y]) ||
4977 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4980 boolean can_turn_left =
4981 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4982 IS_AMOEBOID(Feld[left_x][left_y])));
4984 boolean can_turn_right =
4985 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4986 IS_AMOEBOID(Feld[right_x][right_y])));
4988 if (can_turn_left && can_turn_right)
4989 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4990 else if (can_turn_left)
4991 MovDir[x][y] = left_dir;
4993 MovDir[x][y] = right_dir;
4996 if (MovDir[x][y] != old_move_dir)
4999 else if (element == EL_BALLOON)
5001 MovDir[x][y] = game.balloon_dir;
5004 else if (element == EL_SPRING)
5007 if (MovDir[x][y] & MV_HORIZONTAL &&
5008 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
5009 MovDir[x][y] = MV_NO_MOVING;
5011 if (MovDir[x][y] & MV_HORIZONTAL &&
5012 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5013 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5014 MovDir[x][y] = MV_NO_MOVING;
5019 else if (element == EL_ROBOT ||
5020 element == EL_SATELLITE ||
5021 element == EL_PENGUIN)
5023 int attr_x = -1, attr_y = -1;
5034 for (i = 0; i < MAX_PLAYERS; i++)
5036 struct PlayerInfo *player = &stored_player[i];
5037 int jx = player->jx, jy = player->jy;
5039 if (!player->active)
5043 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5052 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5053 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5054 game.engine_version < VERSION_IDENT(3,1,0,0)))
5056 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5063 if (element == EL_PENGUIN)
5066 static int xy[4][2] =
5074 for (i = 0; i < NUM_DIRECTIONS; i++)
5076 int ex = x + xy[i][0];
5077 int ey = y + xy[i][1];
5079 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5088 MovDir[x][y] = MV_NO_MOVING;
5090 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5091 else if (attr_x > x)
5092 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5094 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5095 else if (attr_y > y)
5096 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5098 if (element == EL_ROBOT)
5102 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5103 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5104 Moving2Blocked(x, y, &newx, &newy);
5106 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5107 MovDelay[x][y] = 8 + 8 * !RND(3);
5109 MovDelay[x][y] = 16;
5111 else if (element == EL_PENGUIN)
5117 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5119 boolean first_horiz = RND(2);
5120 int new_move_dir = MovDir[x][y];
5123 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5124 Moving2Blocked(x, y, &newx, &newy);
5126 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5130 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5131 Moving2Blocked(x, y, &newx, &newy);
5133 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5136 MovDir[x][y] = old_move_dir;
5140 else /* (element == EL_SATELLITE) */
5146 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5148 boolean first_horiz = RND(2);
5149 int new_move_dir = MovDir[x][y];
5152 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5153 Moving2Blocked(x, y, &newx, &newy);
5155 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5159 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5160 Moving2Blocked(x, y, &newx, &newy);
5162 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5165 MovDir[x][y] = old_move_dir;
5170 else if (move_pattern == MV_TURNING_LEFT ||
5171 move_pattern == MV_TURNING_RIGHT ||
5172 move_pattern == MV_TURNING_LEFT_RIGHT ||
5173 move_pattern == MV_TURNING_RIGHT_LEFT ||
5174 move_pattern == MV_TURNING_RANDOM ||
5175 move_pattern == MV_ALL_DIRECTIONS)
5177 boolean can_turn_left =
5178 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5179 boolean can_turn_right =
5180 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5182 #if USE_CAN_MOVE_NOT_MOVING
5183 if (element_info[element].move_stepsize == 0) /* not moving */
5187 if (move_pattern == MV_TURNING_LEFT)
5188 MovDir[x][y] = left_dir;
5189 else if (move_pattern == MV_TURNING_RIGHT)
5190 MovDir[x][y] = right_dir;
5191 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5192 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5193 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5194 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5195 else if (move_pattern == MV_TURNING_RANDOM)
5196 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5197 can_turn_right && !can_turn_left ? right_dir :
5198 RND(2) ? left_dir : right_dir);
5199 else if (can_turn_left && can_turn_right)
5200 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5201 else if (can_turn_left)
5202 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5203 else if (can_turn_right)
5204 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5206 MovDir[x][y] = back_dir;
5208 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5210 else if (move_pattern == MV_HORIZONTAL ||
5211 move_pattern == MV_VERTICAL)
5213 if (move_pattern & old_move_dir)
5214 MovDir[x][y] = back_dir;
5215 else if (move_pattern == MV_HORIZONTAL)
5216 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5217 else if (move_pattern == MV_VERTICAL)
5218 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5220 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5222 else if (move_pattern & MV_ANY_DIRECTION)
5224 MovDir[x][y] = move_pattern;
5225 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5227 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5229 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5230 MovDir[x][y] = left_dir;
5231 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5232 MovDir[x][y] = right_dir;
5234 if (MovDir[x][y] != old_move_dir)
5235 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5237 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5239 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5240 MovDir[x][y] = right_dir;
5241 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5242 MovDir[x][y] = left_dir;
5244 if (MovDir[x][y] != old_move_dir)
5245 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5247 else if (move_pattern == MV_TOWARDS_PLAYER ||
5248 move_pattern == MV_AWAY_FROM_PLAYER)
5250 int attr_x = -1, attr_y = -1;
5252 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5263 for (i = 0; i < MAX_PLAYERS; i++)
5265 struct PlayerInfo *player = &stored_player[i];
5266 int jx = player->jx, jy = player->jy;
5268 if (!player->active)
5272 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5280 MovDir[x][y] = MV_NO_MOVING;
5282 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5283 else if (attr_x > x)
5284 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5286 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5287 else if (attr_y > y)
5288 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5290 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5292 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5294 boolean first_horiz = RND(2);
5295 int new_move_dir = MovDir[x][y];
5297 #if USE_CAN_MOVE_NOT_MOVING
5298 if (element_info[element].move_stepsize == 0) /* not moving */
5300 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5301 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5308 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5309 Moving2Blocked(x, y, &newx, &newy);
5311 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5315 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5316 Moving2Blocked(x, y, &newx, &newy);
5318 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5321 MovDir[x][y] = old_move_dir;
5324 else if (move_pattern == MV_WHEN_PUSHED ||
5325 move_pattern == MV_WHEN_DROPPED)
5327 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5328 MovDir[x][y] = MV_NO_MOVING;
5332 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5334 static int test_xy[7][2] =
5344 static int test_dir[7] =
5354 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5355 int move_preference = -1000000; /* start with very low preference */
5356 int new_move_dir = MV_NO_MOVING;
5357 int start_test = RND(4);
5360 for (i = 0; i < NUM_DIRECTIONS; i++)
5362 int move_dir = test_dir[start_test + i];
5363 int move_dir_preference;
5365 xx = x + test_xy[start_test + i][0];
5366 yy = y + test_xy[start_test + i][1];
5368 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5369 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5371 new_move_dir = move_dir;
5376 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5379 move_dir_preference = -1 * RunnerVisit[xx][yy];
5380 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5381 move_dir_preference = PlayerVisit[xx][yy];
5383 if (move_dir_preference > move_preference)
5385 /* prefer field that has not been visited for the longest time */
5386 move_preference = move_dir_preference;
5387 new_move_dir = move_dir;
5389 else if (move_dir_preference == move_preference &&
5390 move_dir == old_move_dir)
5392 /* prefer last direction when all directions are preferred equally */
5393 move_preference = move_dir_preference;
5394 new_move_dir = move_dir;
5398 MovDir[x][y] = new_move_dir;
5399 if (old_move_dir != new_move_dir)
5402 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5410 static void TurnRound(int x, int y)
5412 int direction = MovDir[x][y];
5415 GfxDir[x][y] = MovDir[x][y];
5421 GfxDir[x][y] = MovDir[x][y];
5424 if (direction != MovDir[x][y])
5429 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5432 GfxAction[x][y] = ACTION_WAITING;
5436 static boolean JustBeingPushed(int x, int y)
5440 for (i = 0; i < MAX_PLAYERS; i++)
5442 struct PlayerInfo *player = &stored_player[i];
5444 if (player->active && player->is_pushing && player->MovPos)
5446 int next_jx = player->jx + (player->jx - player->last_jx);
5447 int next_jy = player->jy + (player->jy - player->last_jy);
5449 if (x == next_jx && y == next_jy)
5457 void StartMoving(int x, int y)
5460 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5462 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5463 int element = Feld[x][y];
5469 if (MovDelay[x][y] == 0)
5470 GfxAction[x][y] = ACTION_DEFAULT;
5472 /* !!! this should be handled more generic (not only for mole) !!! */
5473 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5474 GfxAction[x][y] = ACTION_DEFAULT;
5477 if (CAN_FALL(element) && y < lev_fieldy - 1)
5479 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5480 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5481 if (JustBeingPushed(x, y))
5484 if (element == EL_QUICKSAND_FULL)
5486 if (IS_FREE(x, y + 1))
5488 InitMovingField(x, y, MV_DOWN);
5489 started_moving = TRUE;
5491 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5492 Store[x][y] = EL_ROCK;
5494 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5496 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5499 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5501 if (!MovDelay[x][y])
5502 MovDelay[x][y] = TILEY + 1;
5511 Feld[x][y] = EL_QUICKSAND_EMPTY;
5512 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5513 Store[x][y + 1] = Store[x][y];
5516 PlayLevelSoundAction(x, y, ACTION_FILLING);
5518 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5522 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5523 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5525 InitMovingField(x, y, MV_DOWN);
5526 started_moving = TRUE;
5528 Feld[x][y] = EL_QUICKSAND_FILLING;
5529 Store[x][y] = element;
5531 PlayLevelSoundAction(x, y, ACTION_FILLING);
5533 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5536 else if (element == EL_MAGIC_WALL_FULL)
5538 if (IS_FREE(x, y + 1))
5540 InitMovingField(x, y, MV_DOWN);
5541 started_moving = TRUE;
5543 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5544 Store[x][y] = EL_CHANGED(Store[x][y]);
5546 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5548 if (!MovDelay[x][y])
5549 MovDelay[x][y] = TILEY/4 + 1;
5558 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5559 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5560 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5564 else if (element == EL_BD_MAGIC_WALL_FULL)
5566 if (IS_FREE(x, y + 1))
5568 InitMovingField(x, y, MV_DOWN);
5569 started_moving = TRUE;
5571 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5572 Store[x][y] = EL_CHANGED2(Store[x][y]);
5574 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5576 if (!MovDelay[x][y])
5577 MovDelay[x][y] = TILEY/4 + 1;
5586 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5587 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5588 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5592 else if (CAN_PASS_MAGIC_WALL(element) &&
5593 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5594 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5596 InitMovingField(x, y, MV_DOWN);
5597 started_moving = TRUE;
5600 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5601 EL_BD_MAGIC_WALL_FILLING);
5602 Store[x][y] = element;
5605 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5607 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5610 SplashAcid(x, y + 1);
5612 InitMovingField(x, y, MV_DOWN);
5613 started_moving = TRUE;
5615 Store[x][y] = EL_ACID;
5617 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5618 GfxAction[x][y + 1] = ACTION_ACTIVE;
5622 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5623 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5625 #if USE_IMPACT_BUGFIX
5626 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5627 CAN_FALL(element) && WasJustFalling[x][y] &&
5628 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5630 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5631 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5632 (Feld[x][y + 1] == EL_BLOCKED)))
5634 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5635 CAN_SMASH(element) && WasJustFalling[x][y] &&
5636 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5638 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5639 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5640 (Feld[x][y + 1] == EL_BLOCKED)))
5645 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5646 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5647 WasJustMoving[x][y] && !Pushed[x][y + 1])
5649 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5650 WasJustMoving[x][y])
5655 /* this is needed for a special case not covered by calling "Impact()"
5656 from "ContinueMoving()": if an element moves to a tile directly below
5657 another element which was just falling on that tile (which was empty
5658 in the previous frame), the falling element above would just stop
5659 instead of smashing the element below (in previous version, the above
5660 element was just checked for "moving" instead of "falling", resulting
5661 in incorrect smashes caused by horizontal movement of the above
5662 element; also, the case of the player being the element to smash was
5663 simply not covered here... :-/ ) */
5666 WasJustMoving[x][y] = 0;
5667 WasJustFalling[x][y] = 0;
5670 CheckCollision[x][y] = 0;
5673 if (IS_PLAYER(x, y + 1))
5674 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5679 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5681 if (MovDir[x][y] == MV_NO_MOVING)
5683 InitMovingField(x, y, MV_DOWN);
5684 started_moving = TRUE;
5687 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5689 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5690 MovDir[x][y] = MV_DOWN;
5692 InitMovingField(x, y, MV_DOWN);
5693 started_moving = TRUE;
5695 else if (element == EL_AMOEBA_DROP)
5697 Feld[x][y] = EL_AMOEBA_GROWING;
5698 Store[x][y] = EL_AMOEBA_WET;
5700 /* Store[x][y + 1] must be zero, because:
5701 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5704 #if OLD_GAME_BEHAVIOUR
5705 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5707 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5708 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5709 element != EL_DX_SUPABOMB)
5712 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5713 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5714 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5715 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5718 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5719 (IS_FREE(x - 1, y + 1) ||
5720 Feld[x - 1][y + 1] == EL_ACID));
5721 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5722 (IS_FREE(x + 1, y + 1) ||
5723 Feld[x + 1][y + 1] == EL_ACID));
5724 boolean can_fall_any = (can_fall_left || can_fall_right);
5725 boolean can_fall_both = (can_fall_left && can_fall_right);
5727 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5729 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5731 if (slippery_type == SLIPPERY_ONLY_LEFT)
5732 can_fall_right = FALSE;
5733 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5734 can_fall_left = FALSE;
5735 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5736 can_fall_right = FALSE;
5737 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5738 can_fall_left = FALSE;
5740 can_fall_any = (can_fall_left || can_fall_right);
5741 can_fall_both = (can_fall_left && can_fall_right);
5744 #if USE_NEW_SP_SLIPPERY
5745 /* !!! better use the same properties as for custom elements here !!! */
5746 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5747 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5749 can_fall_right = FALSE; /* slip down on left side */
5750 can_fall_both = FALSE;
5757 if (game.emulation == EMU_BOULDERDASH ||
5758 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5759 can_fall_right = FALSE; /* slip down on left side */
5761 can_fall_left = !(can_fall_right = RND(2));
5763 can_fall_both = FALSE;
5770 if (can_fall_both &&
5771 (game.emulation != EMU_BOULDERDASH &&
5772 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5773 can_fall_left = !(can_fall_right = RND(2));
5776 /* if not determined otherwise, prefer left side for slipping down */
5777 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5778 started_moving = TRUE;
5782 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5784 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5787 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5788 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5789 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5790 int belt_dir = game.belt_dir[belt_nr];
5792 if ((belt_dir == MV_LEFT && left_is_free) ||
5793 (belt_dir == MV_RIGHT && right_is_free))
5796 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5799 InitMovingField(x, y, belt_dir);
5800 started_moving = TRUE;
5803 Pushed[x][y] = TRUE;
5804 Pushed[nextx][y] = TRUE;
5807 GfxAction[x][y] = ACTION_DEFAULT;
5811 MovDir[x][y] = 0; /* if element was moving, stop it */
5816 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5818 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5820 if (CAN_MOVE(element) && !started_moving)
5823 int move_pattern = element_info[element].move_pattern;
5828 if (MovDir[x][y] == MV_NO_MOVING)
5830 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5831 x, y, element, element_info[element].token_name);
5832 printf("StartMoving(): This should never happen!\n");
5837 Moving2Blocked(x, y, &newx, &newy);
5840 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5843 if ((element == EL_SATELLITE ||
5844 element == EL_BALLOON ||
5845 element == EL_SPRING)
5846 && JustBeingPushed(x, y))
5853 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5854 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5856 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5857 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5858 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5862 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5863 element, element_info[element].token_name,
5864 WasJustMoving[x][y],
5865 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5866 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5867 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5868 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5872 WasJustMoving[x][y] = 0;
5875 CheckCollision[x][y] = 0;
5877 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5880 if (Feld[x][y] != element) /* element has changed */
5882 element = Feld[x][y];
5883 move_pattern = element_info[element].move_pattern;
5885 if (!CAN_MOVE(element))
5889 if (Feld[x][y] != element) /* element has changed */
5897 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5898 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5900 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5902 Moving2Blocked(x, y, &newx, &newy);
5903 if (Feld[newx][newy] == EL_BLOCKED)
5904 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5910 if (FrameCounter < 1 && x == 0 && y == 29)
5911 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5914 if (!MovDelay[x][y]) /* start new movement phase */
5916 /* all objects that can change their move direction after each step
5917 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5919 if (element != EL_YAMYAM &&
5920 element != EL_DARK_YAMYAM &&
5921 element != EL_PACMAN &&
5922 !(move_pattern & MV_ANY_DIRECTION) &&
5923 move_pattern != MV_TURNING_LEFT &&
5924 move_pattern != MV_TURNING_RIGHT &&
5925 move_pattern != MV_TURNING_LEFT_RIGHT &&
5926 move_pattern != MV_TURNING_RIGHT_LEFT &&
5927 move_pattern != MV_TURNING_RANDOM)
5932 if (FrameCounter < 1 && x == 0 && y == 29)
5933 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5936 if (MovDelay[x][y] && (element == EL_BUG ||
5937 element == EL_SPACESHIP ||
5938 element == EL_SP_SNIKSNAK ||
5939 element == EL_SP_ELECTRON ||
5940 element == EL_MOLE))
5941 DrawLevelField(x, y);
5945 if (MovDelay[x][y]) /* wait some time before next movement */
5950 if (element == EL_YAMYAM)
5953 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5954 DrawLevelElementAnimation(x, y, element);
5958 if (MovDelay[x][y]) /* element still has to wait some time */
5961 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5962 ResetGfxAnimation(x, y);
5966 if (GfxAction[x][y] != ACTION_WAITING)
5967 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5969 GfxAction[x][y] = ACTION_WAITING;
5973 if (element == EL_ROBOT ||
5975 element == EL_PACMAN ||
5977 element == EL_YAMYAM ||
5978 element == EL_DARK_YAMYAM)
5981 DrawLevelElementAnimation(x, y, element);
5983 DrawLevelElementAnimationIfNeeded(x, y, element);
5985 PlayLevelSoundAction(x, y, ACTION_WAITING);
5987 else if (element == EL_SP_ELECTRON)
5988 DrawLevelElementAnimationIfNeeded(x, y, element);
5989 else if (element == EL_DRAGON)
5992 int dir = MovDir[x][y];
5993 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5994 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5995 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5996 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5997 dir == MV_UP ? IMG_FLAMES_1_UP :
5998 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5999 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6002 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
6005 GfxAction[x][y] = ACTION_ATTACKING;
6007 if (IS_PLAYER(x, y))
6008 DrawPlayerField(x, y);
6010 DrawLevelField(x, y);
6012 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6014 for (i = 1; i <= 3; i++)
6016 int xx = x + i * dx;
6017 int yy = y + i * dy;
6018 int sx = SCREENX(xx);
6019 int sy = SCREENY(yy);
6020 int flame_graphic = graphic + (i - 1);
6022 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6027 int flamed = MovingOrBlocked2Element(xx, yy);
6031 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6033 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6034 RemoveMovingField(xx, yy);
6036 RemoveField(xx, yy);
6038 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6041 RemoveMovingField(xx, yy);
6045 if (ChangeDelay[xx][yy])
6046 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6047 Feld[xx][yy] == EL_BLOCKED));
6051 ChangeDelay[xx][yy] = 0;
6053 Feld[xx][yy] = EL_FLAMES;
6054 if (IN_SCR_FIELD(sx, sy))
6056 DrawLevelFieldCrumbledSand(xx, yy);
6057 DrawGraphic(sx, sy, flame_graphic, frame);
6062 if (Feld[xx][yy] == EL_FLAMES)
6063 Feld[xx][yy] = EL_EMPTY;
6064 DrawLevelField(xx, yy);
6069 if (MovDelay[x][y]) /* element still has to wait some time */
6071 PlayLevelSoundAction(x, y, ACTION_WAITING);
6077 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6078 for all other elements GfxAction will be set by InitMovingField() */
6079 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6080 GfxAction[x][y] = ACTION_MOVING;
6084 /* now make next step */
6086 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6088 if (DONT_COLLIDE_WITH(element) &&
6089 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6090 !PLAYER_ENEMY_PROTECTED(newx, newy))
6093 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6097 /* player killed by element which is deadly when colliding with */
6099 KillHero(PLAYERINFO(newx, newy));
6106 else if (CAN_MOVE_INTO_ACID(element) &&
6107 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6108 (MovDir[x][y] == MV_DOWN ||
6109 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6111 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6112 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6116 else if ((element == EL_PENGUIN ||
6117 element == EL_ROBOT ||
6118 element == EL_SATELLITE ||
6119 element == EL_BALLOON ||
6120 IS_CUSTOM_ELEMENT(element)) &&
6121 IN_LEV_FIELD(newx, newy) &&
6122 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6125 SplashAcid(newx, newy);
6126 Store[x][y] = EL_ACID;
6128 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6130 if (Feld[newx][newy] == EL_EXIT_OPEN)
6134 DrawLevelField(x, y);
6136 Feld[x][y] = EL_EMPTY;
6137 DrawLevelField(x, y);
6140 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6141 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6142 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6144 local_player->friends_still_needed--;
6145 if (!local_player->friends_still_needed &&
6146 !local_player->GameOver && AllPlayersGone)
6147 local_player->LevelSolved = local_player->GameOver = TRUE;
6151 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6153 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6154 DrawLevelField(newx, newy);
6156 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6158 else if (!IS_FREE(newx, newy))
6160 GfxAction[x][y] = ACTION_WAITING;
6162 if (IS_PLAYER(x, y))
6163 DrawPlayerField(x, y);
6165 DrawLevelField(x, y);
6170 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6172 if (IS_FOOD_PIG(Feld[newx][newy]))
6174 if (IS_MOVING(newx, newy))
6175 RemoveMovingField(newx, newy);
6178 Feld[newx][newy] = EL_EMPTY;
6179 DrawLevelField(newx, newy);
6182 PlayLevelSound(x, y, SND_PIG_DIGGING);
6184 else if (!IS_FREE(newx, newy))
6186 if (IS_PLAYER(x, y))
6187 DrawPlayerField(x, y);
6189 DrawLevelField(x, y);
6198 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6201 else if (IS_CUSTOM_ELEMENT(element) &&
6202 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6206 !IS_FREE(newx, newy)
6211 int new_element = Feld[newx][newy];
6214 printf("::: '%s' digs '%s' [%d]\n",
6215 element_info[element].token_name,
6216 element_info[Feld[newx][newy]].token_name,
6217 StorePlayer[newx][newy]);
6220 if (!IS_FREE(newx, newy))
6222 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6223 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6226 /* no element can dig solid indestructible elements */
6227 if (IS_INDESTRUCTIBLE(new_element) &&
6228 !IS_DIGGABLE(new_element) &&
6229 !IS_COLLECTIBLE(new_element))
6232 if (AmoebaNr[newx][newy] &&
6233 (new_element == EL_AMOEBA_FULL ||
6234 new_element == EL_BD_AMOEBA ||
6235 new_element == EL_AMOEBA_GROWING))
6237 AmoebaCnt[AmoebaNr[newx][newy]]--;
6238 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6241 if (IS_MOVING(newx, newy))
6242 RemoveMovingField(newx, newy);
6245 RemoveField(newx, newy);
6246 DrawLevelField(newx, newy);
6249 /* if digged element was about to explode, prevent the explosion */
6250 ExplodeField[newx][newy] = EX_TYPE_NONE;
6252 PlayLevelSoundAction(x, y, action);
6257 Store[newx][newy] = EL_EMPTY;
6258 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6260 #if USE_CHANGE_TO_TRIGGERED
6261 int move_leave_element = element_info[element].move_leave_element;
6263 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6264 new_element : move_leave_element);
6266 Store[newx][newy] = element_info[element].move_leave_element;
6270 Store[newx][newy] = EL_EMPTY;
6271 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6272 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6273 Store[newx][newy] = element_info[element].move_leave_element;
6276 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6277 element_info[element].can_leave_element = TRUE;
6280 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6282 RunnerVisit[x][y] = FrameCounter;
6283 PlayerVisit[x][y] /= 8; /* expire player visit path */
6289 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6291 if (!IS_FREE(newx, newy))
6293 if (IS_PLAYER(x, y))
6294 DrawPlayerField(x, y);
6296 DrawLevelField(x, y);
6302 boolean wanna_flame = !RND(10);
6303 int dx = newx - x, dy = newy - y;
6304 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6305 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6306 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6307 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6308 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6309 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6312 IS_CLASSIC_ENEMY(element1) ||
6313 IS_CLASSIC_ENEMY(element2)) &&
6314 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6315 element1 != EL_FLAMES && element2 != EL_FLAMES)
6318 ResetGfxAnimation(x, y);
6319 GfxAction[x][y] = ACTION_ATTACKING;
6322 if (IS_PLAYER(x, y))
6323 DrawPlayerField(x, y);
6325 DrawLevelField(x, y);
6327 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6329 MovDelay[x][y] = 50;
6333 RemoveField(newx, newy);
6335 Feld[newx][newy] = EL_FLAMES;
6336 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6339 RemoveField(newx1, newy1);
6341 Feld[newx1][newy1] = EL_FLAMES;
6343 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6346 RemoveField(newx2, newy2);
6348 Feld[newx2][newy2] = EL_FLAMES;
6355 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6356 Feld[newx][newy] == EL_DIAMOND)
6358 if (IS_MOVING(newx, newy))
6359 RemoveMovingField(newx, newy);
6362 Feld[newx][newy] = EL_EMPTY;
6363 DrawLevelField(newx, newy);
6366 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6368 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6369 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6371 if (AmoebaNr[newx][newy])
6373 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6374 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6375 Feld[newx][newy] == EL_BD_AMOEBA)
6376 AmoebaCnt[AmoebaNr[newx][newy]]--;
6381 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6383 if (IS_MOVING(newx, newy))
6386 RemoveMovingField(newx, newy);
6390 Feld[newx][newy] = EL_EMPTY;
6391 DrawLevelField(newx, newy);
6394 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6396 else if ((element == EL_PACMAN || element == EL_MOLE)
6397 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6399 if (AmoebaNr[newx][newy])
6401 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6402 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6403 Feld[newx][newy] == EL_BD_AMOEBA)
6404 AmoebaCnt[AmoebaNr[newx][newy]]--;
6407 if (element == EL_MOLE)
6409 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6410 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6412 ResetGfxAnimation(x, y);
6413 GfxAction[x][y] = ACTION_DIGGING;
6414 DrawLevelField(x, y);
6416 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6418 return; /* wait for shrinking amoeba */
6420 else /* element == EL_PACMAN */
6422 Feld[newx][newy] = EL_EMPTY;
6423 DrawLevelField(newx, newy);
6424 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6427 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6428 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6429 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6431 /* wait for shrinking amoeba to completely disappear */
6434 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6436 /* object was running against a wall */
6441 if (move_pattern & MV_ANY_DIRECTION &&
6442 move_pattern == MovDir[x][y])
6444 int blocking_element =
6445 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6448 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6449 element_info[element].token_name,
6450 element_info[blocking_element].token_name,
6454 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6457 element = Feld[x][y]; /* element might have changed */
6462 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6463 DrawLevelElementAnimation(x, y, element);
6465 if (element == EL_BUG ||
6466 element == EL_SPACESHIP ||
6467 element == EL_SP_SNIKSNAK)
6468 DrawLevelField(x, y);
6469 else if (element == EL_MOLE)
6470 DrawLevelField(x, y);
6471 else if (element == EL_BD_BUTTERFLY ||
6472 element == EL_BD_FIREFLY)
6473 DrawLevelElementAnimationIfNeeded(x, y, element);
6474 else if (element == EL_SATELLITE)
6475 DrawLevelElementAnimationIfNeeded(x, y, element);
6476 else if (element == EL_SP_ELECTRON)
6477 DrawLevelElementAnimationIfNeeded(x, y, element);
6480 if (DONT_TOUCH(element))
6481 TestIfBadThingTouchesHero(x, y);
6484 PlayLevelSoundAction(x, y, ACTION_WAITING);
6490 InitMovingField(x, y, MovDir[x][y]);
6492 PlayLevelSoundAction(x, y, ACTION_MOVING);
6496 ContinueMoving(x, y);
6503 void ContinueMoving(int x, int y)
6505 int element = Feld[x][y];
6506 int stored = Store[x][y];
6507 struct ElementInfo *ei = &element_info[element];
6508 int direction = MovDir[x][y];
6509 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6510 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6511 int newx = x + dx, newy = y + dy;
6513 int nextx = newx + dx, nexty = newy + dy;
6516 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6517 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6519 boolean pushed_by_player = Pushed[x][y];
6521 boolean last_line = (newy == lev_fieldy - 1);
6523 MovPos[x][y] += getElementMoveStepsize(x, y);
6526 if (pushed_by_player && IS_PLAYER(x, y))
6528 /* special case: moving object pushed by player */
6529 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6532 if (pushed_by_player) /* special case: moving object pushed by player */
6533 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6536 if (ABS(MovPos[x][y]) < TILEX)
6538 DrawLevelField(x, y);
6540 return; /* element is still moving */
6543 /* element reached destination field */
6545 Feld[x][y] = EL_EMPTY;
6546 Feld[newx][newy] = element;
6547 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6550 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6552 element = Feld[newx][newy] = EL_ACID;
6555 else if (element == EL_MOLE)
6557 Feld[x][y] = EL_SAND;
6559 DrawLevelFieldCrumbledSandNeighbours(x, y);
6561 else if (element == EL_QUICKSAND_FILLING)
6563 element = Feld[newx][newy] = get_next_element(element);
6564 Store[newx][newy] = Store[x][y];
6566 else if (element == EL_QUICKSAND_EMPTYING)
6568 Feld[x][y] = get_next_element(element);
6569 element = Feld[newx][newy] = Store[x][y];
6571 else if (element == EL_MAGIC_WALL_FILLING)
6573 element = Feld[newx][newy] = get_next_element(element);
6574 if (!game.magic_wall_active)
6575 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6576 Store[newx][newy] = Store[x][y];
6578 else if (element == EL_MAGIC_WALL_EMPTYING)
6580 Feld[x][y] = get_next_element(element);
6581 if (!game.magic_wall_active)
6582 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6583 element = Feld[newx][newy] = Store[x][y];
6585 else if (element == EL_BD_MAGIC_WALL_FILLING)
6587 element = Feld[newx][newy] = get_next_element(element);
6588 if (!game.magic_wall_active)
6589 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6590 Store[newx][newy] = Store[x][y];
6592 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6594 Feld[x][y] = get_next_element(element);
6595 if (!game.magic_wall_active)
6596 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6597 element = Feld[newx][newy] = Store[x][y];
6599 else if (element == EL_AMOEBA_DROPPING)
6601 Feld[x][y] = get_next_element(element);
6602 element = Feld[newx][newy] = Store[x][y];
6604 else if (element == EL_SOKOBAN_OBJECT)
6607 Feld[x][y] = Back[x][y];
6609 if (Back[newx][newy])
6610 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6612 Back[x][y] = Back[newx][newy] = 0;
6615 else if (Store[x][y] == EL_ACID)
6617 element = Feld[newx][newy] = EL_ACID;
6621 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6622 ei->move_leave_element != EL_EMPTY &&
6623 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6624 Store[x][y] != EL_EMPTY))
6626 /* some elements can leave other elements behind after moving */
6628 Feld[x][y] = ei->move_leave_element;
6629 InitField(x, y, FALSE);
6631 if (GFX_CRUMBLED(Feld[x][y]))
6632 DrawLevelFieldCrumbledSandNeighbours(x, y);
6636 Store[x][y] = EL_EMPTY;
6640 MovDelay[newx][newy] = 0;
6642 if (CAN_CHANGE(element))
6644 /* copy element change control values to new field */
6645 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6646 ChangePage[newx][newy] = ChangePage[x][y];
6647 Changed[newx][newy] = Changed[x][y];
6648 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6651 ChangeDelay[x][y] = 0;
6652 ChangePage[x][y] = -1;
6653 Changed[x][y] = FALSE;
6654 ChangeEvent[x][y] = -1;
6656 /* copy animation control values to new field */
6657 GfxFrame[newx][newy] = GfxFrame[x][y];
6658 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6659 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6660 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6662 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6665 /* do this after checking for left-behind element */
6666 ResetGfxAnimation(x, y); /* reset animation values for old field */
6670 /* some elements can leave other elements behind after moving */
6672 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6673 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6674 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6676 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6677 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6681 int move_leave_element = ei->move_leave_element;
6683 #if USE_CHANGE_TO_TRIGGERED
6684 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6685 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6686 move_leave_element = stored;
6689 Feld[x][y] = move_leave_element;
6691 #if USE_PREVIOUS_MOVE_DIR
6692 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6693 MovDir[x][y] = direction;
6696 InitField(x, y, FALSE);
6698 if (GFX_CRUMBLED(Feld[x][y]))
6699 DrawLevelFieldCrumbledSandNeighbours(x, y);
6701 if (ELEM_IS_PLAYER(move_leave_element))
6702 RelocatePlayer(x, y, move_leave_element);
6707 /* some elements can leave other elements behind after moving */
6708 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6709 ei->move_leave_element != EL_EMPTY &&
6710 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6711 ei->can_leave_element_last))
6713 Feld[x][y] = ei->move_leave_element;
6714 InitField(x, y, FALSE);
6716 if (GFX_CRUMBLED(Feld[x][y]))
6717 DrawLevelFieldCrumbledSandNeighbours(x, y);
6720 ei->can_leave_element_last = ei->can_leave_element;
6721 ei->can_leave_element = FALSE;
6725 /* do this after checking for left-behind element */
6726 ResetGfxAnimation(x, y); /* reset animation values for old field */
6730 /* 2.1.1 (does not work correctly for spring) */
6731 if (!CAN_MOVE(element))
6732 MovDir[newx][newy] = 0;
6736 /* (does not work for falling objects that slide horizontally) */
6737 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6738 MovDir[newx][newy] = 0;
6741 if (!CAN_MOVE(element) ||
6742 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6743 MovDir[newx][newy] = 0;
6747 if (!CAN_MOVE(element) ||
6748 (CAN_FALL(element) && direction == MV_DOWN))
6749 GfxDir[x][y] = MovDir[newx][newy] = 0;
6751 if (!CAN_MOVE(element) ||
6752 (CAN_FALL(element) && direction == MV_DOWN &&
6753 (element == EL_SPRING ||
6754 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6755 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6756 GfxDir[x][y] = MovDir[newx][newy] = 0;
6762 DrawLevelField(x, y);
6763 DrawLevelField(newx, newy);
6765 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6767 /* prevent pushed element from moving on in pushed direction */
6768 if (pushed_by_player && CAN_MOVE(element) &&
6769 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6770 !(element_info[element].move_pattern & direction))
6771 TurnRound(newx, newy);
6774 /* prevent elements on conveyor belt from moving on in last direction */
6775 if (pushed_by_conveyor && CAN_FALL(element) &&
6776 direction & MV_HORIZONTAL)
6779 if (CAN_MOVE(element))
6780 InitMovDir(newx, newy);
6782 MovDir[newx][newy] = 0;
6784 MovDir[newx][newy] = 0;
6789 if (!pushed_by_player)
6791 int nextx = newx + dx, nexty = newy + dy;
6792 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6794 WasJustMoving[newx][newy] = 3;
6796 if (CAN_FALL(element) && direction == MV_DOWN)
6797 WasJustFalling[newx][newy] = 3;
6799 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6800 CheckCollision[newx][newy] = 2;
6803 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6805 TestIfBadThingTouchesHero(newx, newy);
6806 TestIfBadThingTouchesFriend(newx, newy);
6808 if (!IS_CUSTOM_ELEMENT(element))
6809 TestIfBadThingTouchesOtherBadThing(newx, newy);
6811 else if (element == EL_PENGUIN)
6812 TestIfFriendTouchesBadThing(newx, newy);
6814 #if USE_NEW_MOVE_STYLE
6816 if (CAN_FALL(element) && direction == MV_DOWN &&
6817 !last_line && IS_PLAYER(x, newy + 1))
6818 printf("::: we would now kill the player [%d]\n", FrameCounter);
6821 /* give the player one last chance (one more frame) to move away */
6822 if (CAN_FALL(element) && direction == MV_DOWN &&
6823 (last_line || (!IS_FREE(x, newy + 1) &&
6824 (!IS_PLAYER(x, newy + 1) ||
6825 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6828 if (CAN_FALL(element) && direction == MV_DOWN &&
6829 (last_line || !IS_FREE(x, newy + 1)))
6837 if (pushed_by_player && !game.use_change_when_pushing_bug)
6839 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6842 if (pushed_by_player)
6847 int dig_side = MV_DIR_OPPOSITE(direction);
6849 static int trigger_sides[4] =
6851 CH_SIDE_RIGHT, /* moving left */
6852 CH_SIDE_LEFT, /* moving right */
6853 CH_SIDE_BOTTOM, /* moving up */
6854 CH_SIDE_TOP, /* moving down */
6856 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6858 struct PlayerInfo *player = PLAYERINFO(x, y);
6860 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6861 player->index_bit, dig_side);
6862 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6863 player->index_bit, dig_side);
6868 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6872 if (ChangePage[newx][newy] != -1) /* delayed change */
6873 ChangeElement(newx, newy, ChangePage[newx][newy]);
6878 TestIfElementHitsCustomElement(newx, newy, direction);
6882 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6884 int hitting_element = Feld[newx][newy];
6886 /* !!! fix side (direction) orientation here and elsewhere !!! */
6887 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6891 if (IN_LEV_FIELD(nextx, nexty))
6893 int opposite_direction = MV_DIR_OPPOSITE(direction);
6894 int hitting_side = direction;
6895 int touched_side = opposite_direction;
6896 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6897 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6898 MovDir[nextx][nexty] != direction ||
6899 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6905 CheckElementChangeBySide(nextx, nexty, touched_element,
6906 CE_HIT_BY_SOMETHING, opposite_direction);
6908 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6909 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6911 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6913 struct ElementChangeInfo *change =
6914 &element_info[hitting_element].change_page[i];
6916 if (change->can_change &&
6917 change->has_event[CE_HITTING_X] &&
6918 change->trigger_side & touched_side &&
6919 change->trigger_element == touched_element)
6921 CheckElementChangeByPage(newx, newy, hitting_element,
6922 touched_element, CE_HITTING_X, i);
6928 if (IS_CUSTOM_ELEMENT(touched_element) &&
6929 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6931 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6933 struct ElementChangeInfo *change =
6934 &element_info[touched_element].change_page[i];
6936 if (change->can_change &&
6937 change->has_event[CE_HIT_BY_X] &&
6938 change->trigger_side & hitting_side &&
6939 change->trigger_element == hitting_element)
6941 CheckElementChangeByPage(nextx, nexty, touched_element,
6942 hitting_element, CE_HIT_BY_X,i);
6953 TestIfPlayerTouchesCustomElement(newx, newy);
6954 TestIfElementTouchesCustomElement(newx, newy);
6957 int AmoebeNachbarNr(int ax, int ay)
6960 int element = Feld[ax][ay];
6962 static int xy[4][2] =
6970 for (i = 0; i < NUM_DIRECTIONS; i++)
6972 int x = ax + xy[i][0];
6973 int y = ay + xy[i][1];
6975 if (!IN_LEV_FIELD(x, y))
6978 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6979 group_nr = AmoebaNr[x][y];
6985 void AmoebenVereinigen(int ax, int ay)
6987 int i, x, y, xx, yy;
6988 int new_group_nr = AmoebaNr[ax][ay];
6989 static int xy[4][2] =
6997 if (new_group_nr == 0)
7000 for (i = 0; i < NUM_DIRECTIONS; i++)
7005 if (!IN_LEV_FIELD(x, y))
7008 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7009 Feld[x][y] == EL_BD_AMOEBA ||
7010 Feld[x][y] == EL_AMOEBA_DEAD) &&
7011 AmoebaNr[x][y] != new_group_nr)
7013 int old_group_nr = AmoebaNr[x][y];
7015 if (old_group_nr == 0)
7018 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7019 AmoebaCnt[old_group_nr] = 0;
7020 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7021 AmoebaCnt2[old_group_nr] = 0;
7023 for (yy = 0; yy < lev_fieldy; yy++)
7025 for (xx = 0; xx < lev_fieldx; xx++)
7027 if (AmoebaNr[xx][yy] == old_group_nr)
7028 AmoebaNr[xx][yy] = new_group_nr;
7035 void AmoebeUmwandeln(int ax, int ay)
7039 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7041 int group_nr = AmoebaNr[ax][ay];
7046 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7047 printf("AmoebeUmwandeln(): This should never happen!\n");
7052 for (y = 0; y < lev_fieldy; y++)
7054 for (x = 0; x < lev_fieldx; x++)
7056 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7059 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7063 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7064 SND_AMOEBA_TURNING_TO_GEM :
7065 SND_AMOEBA_TURNING_TO_ROCK));
7070 static int xy[4][2] =
7078 for (i = 0; i < NUM_DIRECTIONS; i++)
7083 if (!IN_LEV_FIELD(x, y))
7086 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7088 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7089 SND_AMOEBA_TURNING_TO_GEM :
7090 SND_AMOEBA_TURNING_TO_ROCK));
7097 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7100 int group_nr = AmoebaNr[ax][ay];
7101 boolean done = FALSE;
7106 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7107 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7112 for (y = 0; y < lev_fieldy; y++)
7114 for (x = 0; x < lev_fieldx; x++)
7116 if (AmoebaNr[x][y] == group_nr &&
7117 (Feld[x][y] == EL_AMOEBA_DEAD ||
7118 Feld[x][y] == EL_BD_AMOEBA ||
7119 Feld[x][y] == EL_AMOEBA_GROWING))
7122 Feld[x][y] = new_element;
7123 InitField(x, y, FALSE);
7124 DrawLevelField(x, y);
7131 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7132 SND_BD_AMOEBA_TURNING_TO_ROCK :
7133 SND_BD_AMOEBA_TURNING_TO_GEM));
7136 void AmoebeWaechst(int x, int y)
7138 static unsigned long sound_delay = 0;
7139 static unsigned long sound_delay_value = 0;
7141 if (!MovDelay[x][y]) /* start new growing cycle */
7145 if (DelayReached(&sound_delay, sound_delay_value))
7148 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7150 if (Store[x][y] == EL_BD_AMOEBA)
7151 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7153 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7155 sound_delay_value = 30;
7159 if (MovDelay[x][y]) /* wait some time before growing bigger */
7162 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7164 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7165 6 - MovDelay[x][y]);
7167 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7170 if (!MovDelay[x][y])
7172 Feld[x][y] = Store[x][y];
7174 DrawLevelField(x, y);
7179 void AmoebaDisappearing(int x, int y)
7181 static unsigned long sound_delay = 0;
7182 static unsigned long sound_delay_value = 0;
7184 if (!MovDelay[x][y]) /* start new shrinking cycle */
7188 if (DelayReached(&sound_delay, sound_delay_value))
7189 sound_delay_value = 30;
7192 if (MovDelay[x][y]) /* wait some time before shrinking */
7195 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7197 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7198 6 - MovDelay[x][y]);
7200 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7203 if (!MovDelay[x][y])
7205 Feld[x][y] = EL_EMPTY;
7206 DrawLevelField(x, y);
7208 /* don't let mole enter this field in this cycle;
7209 (give priority to objects falling to this field from above) */
7215 void AmoebeAbleger(int ax, int ay)
7218 int element = Feld[ax][ay];
7219 int graphic = el2img(element);
7220 int newax = ax, neway = ay;
7221 static int xy[4][2] =
7229 if (!level.amoeba_speed)
7231 Feld[ax][ay] = EL_AMOEBA_DEAD;
7232 DrawLevelField(ax, ay);
7236 if (IS_ANIMATED(graphic))
7237 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7239 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7240 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7242 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7245 if (MovDelay[ax][ay])
7249 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7252 int x = ax + xy[start][0];
7253 int y = ay + xy[start][1];
7255 if (!IN_LEV_FIELD(x, y))
7259 if (IS_FREE(x, y) ||
7260 CAN_GROW_INTO(Feld[x][y]) ||
7261 Feld[x][y] == EL_QUICKSAND_EMPTY)
7267 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7268 if (IS_FREE(x, y) ||
7269 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7276 if (newax == ax && neway == ay)
7279 else /* normal or "filled" (BD style) amoeba */
7282 boolean waiting_for_player = FALSE;
7284 for (i = 0; i < NUM_DIRECTIONS; i++)
7286 int j = (start + i) % 4;
7287 int x = ax + xy[j][0];
7288 int y = ay + xy[j][1];
7290 if (!IN_LEV_FIELD(x, y))
7294 if (IS_FREE(x, y) ||
7295 CAN_GROW_INTO(Feld[x][y]) ||
7296 Feld[x][y] == EL_QUICKSAND_EMPTY)
7303 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7304 if (IS_FREE(x, y) ||
7305 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7312 else if (IS_PLAYER(x, y))
7313 waiting_for_player = TRUE;
7316 if (newax == ax && neway == ay) /* amoeba cannot grow */
7319 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7321 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7324 Feld[ax][ay] = EL_AMOEBA_DEAD;
7325 DrawLevelField(ax, ay);
7326 AmoebaCnt[AmoebaNr[ax][ay]]--;
7328 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7330 if (element == EL_AMOEBA_FULL)
7331 AmoebeUmwandeln(ax, ay);
7332 else if (element == EL_BD_AMOEBA)
7333 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7338 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7340 /* amoeba gets larger by growing in some direction */
7342 int new_group_nr = AmoebaNr[ax][ay];
7345 if (new_group_nr == 0)
7347 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7348 printf("AmoebeAbleger(): This should never happen!\n");
7353 AmoebaNr[newax][neway] = new_group_nr;
7354 AmoebaCnt[new_group_nr]++;
7355 AmoebaCnt2[new_group_nr]++;
7357 /* if amoeba touches other amoeba(s) after growing, unify them */
7358 AmoebenVereinigen(newax, neway);
7360 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7362 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7368 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7369 (neway == lev_fieldy - 1 && newax != ax))
7371 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7372 Store[newax][neway] = element;
7374 else if (neway == ay)
7376 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7378 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7380 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7385 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7386 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7387 Store[ax][ay] = EL_AMOEBA_DROP;
7388 ContinueMoving(ax, ay);
7392 DrawLevelField(newax, neway);
7395 void Life(int ax, int ay)
7398 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7400 int element = Feld[ax][ay];
7401 int graphic = el2img(element);
7402 boolean changed = FALSE;
7404 if (IS_ANIMATED(graphic))
7405 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7410 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7411 MovDelay[ax][ay] = life_time;
7413 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7416 if (MovDelay[ax][ay])
7420 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7422 int xx = ax+x1, yy = ay+y1;
7425 if (!IN_LEV_FIELD(xx, yy))
7428 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7430 int x = xx+x2, y = yy+y2;
7432 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7435 if (((Feld[x][y] == element ||
7436 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7438 (IS_FREE(x, y) && Stop[x][y]))
7442 if (xx == ax && yy == ay) /* field in the middle */
7444 if (nachbarn < life[0] || nachbarn > life[1])
7446 Feld[xx][yy] = EL_EMPTY;
7448 DrawLevelField(xx, yy);
7449 Stop[xx][yy] = TRUE;
7454 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7455 { /* free border field */
7456 if (nachbarn >= life[2] && nachbarn <= life[3])
7458 Feld[xx][yy] = element;
7459 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7461 DrawLevelField(xx, yy);
7462 Stop[xx][yy] = TRUE;
7467 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7468 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7469 { /* free border field */
7470 if (nachbarn >= life[2] && nachbarn <= life[3])
7472 Feld[xx][yy] = element;
7473 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7475 DrawLevelField(xx, yy);
7476 Stop[xx][yy] = TRUE;
7484 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7485 SND_GAME_OF_LIFE_GROWING);
7488 static void InitRobotWheel(int x, int y)
7490 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7493 static void RunRobotWheel(int x, int y)
7495 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7498 static void StopRobotWheel(int x, int y)
7500 if (ZX == x && ZY == y)
7504 static void InitTimegateWheel(int x, int y)
7507 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7509 /* another brainless, "type style" bug ... :-( */
7510 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7514 static void RunTimegateWheel(int x, int y)
7516 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7519 void CheckExit(int x, int y)
7521 if (local_player->gems_still_needed > 0 ||
7522 local_player->sokobanfields_still_needed > 0 ||
7523 local_player->lights_still_needed > 0)
7525 int element = Feld[x][y];
7526 int graphic = el2img(element);
7528 if (IS_ANIMATED(graphic))
7529 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7534 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7537 Feld[x][y] = EL_EXIT_OPENING;
7539 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7542 void CheckExitSP(int x, int y)
7544 if (local_player->gems_still_needed > 0)
7546 int element = Feld[x][y];
7547 int graphic = el2img(element);
7549 if (IS_ANIMATED(graphic))
7550 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7555 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7558 Feld[x][y] = EL_SP_EXIT_OPENING;
7560 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7563 static void CloseAllOpenTimegates()
7567 for (y = 0; y < lev_fieldy; y++)
7569 for (x = 0; x < lev_fieldx; x++)
7571 int element = Feld[x][y];
7573 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7575 Feld[x][y] = EL_TIMEGATE_CLOSING;
7577 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7579 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7586 void EdelsteinFunkeln(int x, int y)
7588 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7591 if (Feld[x][y] == EL_BD_DIAMOND)
7594 if (MovDelay[x][y] == 0) /* next animation frame */
7595 MovDelay[x][y] = 11 * !SimpleRND(500);
7597 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7601 if (setup.direct_draw && MovDelay[x][y])
7602 SetDrawtoField(DRAW_BUFFERED);
7604 DrawLevelElementAnimation(x, y, Feld[x][y]);
7606 if (MovDelay[x][y] != 0)
7608 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7609 10 - MovDelay[x][y]);
7611 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7613 if (setup.direct_draw)
7617 dest_x = FX + SCREENX(x) * TILEX;
7618 dest_y = FY + SCREENY(y) * TILEY;
7620 BlitBitmap(drawto_field, window,
7621 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7622 SetDrawtoField(DRAW_DIRECT);
7628 void MauerWaechst(int x, int y)
7632 if (!MovDelay[x][y]) /* next animation frame */
7633 MovDelay[x][y] = 3 * delay;
7635 if (MovDelay[x][y]) /* wait some time before next frame */
7639 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7641 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7642 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7644 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7647 if (!MovDelay[x][y])
7649 if (MovDir[x][y] == MV_LEFT)
7651 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7652 DrawLevelField(x - 1, y);
7654 else if (MovDir[x][y] == MV_RIGHT)
7656 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7657 DrawLevelField(x + 1, y);
7659 else if (MovDir[x][y] == MV_UP)
7661 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7662 DrawLevelField(x, y - 1);
7666 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7667 DrawLevelField(x, y + 1);
7670 Feld[x][y] = Store[x][y];
7672 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7673 DrawLevelField(x, y);
7678 void MauerAbleger(int ax, int ay)
7680 int element = Feld[ax][ay];
7681 int graphic = el2img(element);
7682 boolean oben_frei = FALSE, unten_frei = FALSE;
7683 boolean links_frei = FALSE, rechts_frei = FALSE;
7684 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7685 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7686 boolean new_wall = FALSE;
7688 if (IS_ANIMATED(graphic))
7689 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7691 if (!MovDelay[ax][ay]) /* start building new wall */
7692 MovDelay[ax][ay] = 6;
7694 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7697 if (MovDelay[ax][ay])
7701 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7703 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7705 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7707 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7710 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7711 element == EL_EXPANDABLE_WALL_ANY)
7715 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7716 Store[ax][ay-1] = element;
7717 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7718 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7719 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7720 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7725 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7726 Store[ax][ay+1] = element;
7727 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7728 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7729 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7730 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7735 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7736 element == EL_EXPANDABLE_WALL_ANY ||
7737 element == EL_EXPANDABLE_WALL)
7741 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7742 Store[ax-1][ay] = element;
7743 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7744 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7745 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7746 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7752 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7753 Store[ax+1][ay] = element;
7754 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7755 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7756 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7757 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7762 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7763 DrawLevelField(ax, ay);
7765 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7767 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7768 unten_massiv = TRUE;
7769 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7770 links_massiv = TRUE;
7771 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7772 rechts_massiv = TRUE;
7774 if (((oben_massiv && unten_massiv) ||
7775 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7776 element == EL_EXPANDABLE_WALL) &&
7777 ((links_massiv && rechts_massiv) ||
7778 element == EL_EXPANDABLE_WALL_VERTICAL))
7779 Feld[ax][ay] = EL_WALL;
7783 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7785 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7789 void CheckForDragon(int x, int y)
7792 boolean dragon_found = FALSE;
7793 static int xy[4][2] =
7801 for (i = 0; i < NUM_DIRECTIONS; i++)
7803 for (j = 0; j < 4; j++)
7805 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7807 if (IN_LEV_FIELD(xx, yy) &&
7808 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7810 if (Feld[xx][yy] == EL_DRAGON)
7811 dragon_found = TRUE;
7820 for (i = 0; i < NUM_DIRECTIONS; i++)
7822 for (j = 0; j < 3; j++)
7824 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7826 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7828 Feld[xx][yy] = EL_EMPTY;
7829 DrawLevelField(xx, yy);
7838 static void InitBuggyBase(int x, int y)
7840 int element = Feld[x][y];
7841 int activating_delay = FRAMES_PER_SECOND / 4;
7844 (element == EL_SP_BUGGY_BASE ?
7845 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7846 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7848 element == EL_SP_BUGGY_BASE_ACTIVE ?
7849 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7852 static void WarnBuggyBase(int x, int y)
7855 static int xy[4][2] =
7863 for (i = 0; i < NUM_DIRECTIONS; i++)
7865 int xx = x + xy[i][0], yy = y + xy[i][1];
7867 if (IS_PLAYER(xx, yy))
7869 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7876 static void InitTrap(int x, int y)
7878 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7881 static void ActivateTrap(int x, int y)
7883 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7886 static void ChangeActiveTrap(int x, int y)
7888 int graphic = IMG_TRAP_ACTIVE;
7890 /* if new animation frame was drawn, correct crumbled sand border */
7891 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7892 DrawLevelFieldCrumbledSand(x, y);
7895 static int getSpecialActionElement(int element, int number, int base_element)
7897 return (element != EL_EMPTY ? element :
7898 number != -1 ? base_element + number - 1 :
7902 static int getModifiedActionNumber(int value_old, int value_min, int value_max,
7903 int operator, int operand)
7905 int value_new = (operator == CA_MODE_ADD ? value_old + operand :
7906 operator == CA_MODE_SUBTRACT ? value_old - operand :
7907 operator == CA_MODE_MULTIPLY ? value_old * operand :
7908 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7909 operator == CA_MODE_SET ? operand :
7912 return (value_new < value_min ? value_min :
7913 value_new > value_max ? value_max :
7917 static void ExecuteCustomElementAction(int element, int page)
7919 struct ElementInfo *ei = &element_info[element];
7920 struct ElementChangeInfo *change = &ei->change_page[page];
7921 int action_type = change->action_type;
7922 int action_mode = change->action_mode;
7923 int action_arg = change->action_arg;
7926 if (!change->has_action)
7929 /* ---------- determine action paramater values ---------- */
7931 int action_arg_element =
7932 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7933 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7934 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7937 int action_arg_number =
7938 (action_arg <= CA_ARG_MAX ? action_arg :
7939 action_arg == CA_ARG_NUMBER_MIN ? CA_ARG_MIN :
7940 action_arg == CA_ARG_NUMBER_MAX ? CA_ARG_MAX :
7941 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7942 action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count :
7943 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
7946 /* (for explicit player choice, set invalid value to "no player") */
7947 int action_arg_player_bits =
7948 (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
7949 action_arg >= CA_ARG_PLAYER_1 &&
7950 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7951 action_arg >= CA_ARG_1 &&
7952 action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
7953 action_arg_element >= EL_PLAYER_1 &&
7954 action_arg_element <= EL_PLAYER_4 ?
7955 (1 << (action_arg_element - EL_PLAYER_1)) :
7958 /* (for implicit player choice, set invalid value to "all players") */
7959 int trigger_player_bits =
7960 (change->actual_trigger_player >= EL_PLAYER_1 &&
7961 change->actual_trigger_player <= EL_PLAYER_4 ?
7962 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7965 /* ---------- execute action ---------- */
7974 case CA_EXIT_PLAYER:
7976 for (i = 0; i < MAX_PLAYERS; i++)
7977 if (action_arg_player_bits & (1 << i))
7978 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7983 case CA_KILL_PLAYER:
7985 for (i = 0; i < MAX_PLAYERS; i++)
7986 if (action_arg_player_bits & (1 << i))
7987 KillHero(&stored_player[i]);
7992 case CA_RESTART_LEVEL:
7994 game.restart_level = TRUE;
7999 case CA_SHOW_ENVELOPE:
8001 int element = getSpecialActionElement(action_arg_element,
8002 action_arg_number, EL_ENVELOPE_1);
8004 if (IS_ENVELOPE(element))
8005 local_player->show_envelope = element;
8012 int element = getSpecialActionElement(action_arg_element,
8013 action_arg_number, EL_KEY_1);
8015 if (IS_KEY(element))
8017 for (i = 0; i < MAX_PLAYERS; i++)
8019 if (trigger_player_bits & (1 << i))
8021 stored_player[i].key[KEY_NR(element)] = TRUE;
8023 DrawGameValue_Keys(stored_player[i].key);
8025 redraw_mask |= REDRAW_DOOR_1;
8035 int element = getSpecialActionElement(action_arg_element,
8036 action_arg_number, EL_KEY_1);
8038 if (IS_KEY(element))
8040 for (i = 0; i < MAX_PLAYERS; i++)
8042 if (trigger_player_bits & (1 << i))
8044 stored_player[i].key[KEY_NR(element)] = FALSE;
8046 DrawGameValue_Keys(stored_player[i].key);
8048 redraw_mask |= REDRAW_DOOR_1;
8056 case CA_SET_PLAYER_SPEED:
8058 for (i = 0; i < MAX_PLAYERS; i++)
8060 if (trigger_player_bits & (1 << i))
8062 if (action_arg == CA_ARG_NUMBER_RESET)
8063 stored_player[i].move_delay_value = game.initial_move_delay_value;
8064 else if (action_arg == CA_ARG_NUMBER_NORMAL)
8065 stored_player[i].move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8066 else if (action_arg == CA_ARG_NUMBER_MIN)
8067 stored_player[i].move_delay_value = 16;
8068 else if (action_arg == CA_ARG_NUMBER_MAX)
8069 stored_player[i].move_delay_value = MOVE_DELAY_HIGH_SPEED;
8073 if (action_mode == CA_MODE_ADD)
8075 action_mode = CA_MODE_DIVIDE;
8076 action_arg_number = (1 << action_arg_number);
8078 else if (action_mode == CA_MODE_SUBTRACT)
8080 action_mode = CA_MODE_MULTIPLY;
8081 action_arg_number = (1 << action_arg_number);
8084 int mode = (action_mode == CA_MODE_MULTIPLY ? CA_MODE_DIVIDE :
8085 action_mode == CA_MODE_DIVIDE ? CA_MODE_MULTIPLY :
8088 stored_player[i].move_delay_value =
8089 getModifiedActionNumber(stored_player[i].move_delay_value,
8091 action_mode, action_arg_number);
8102 local_player->gems_still_needed =
8103 getModifiedActionNumber(local_player->gems_still_needed, 0, 999,
8104 action_mode, action_arg_number);
8106 DrawGameValue_Emeralds(local_player->gems_still_needed);
8113 if (level.time > 0) /* only modify limited time value */
8115 TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999,
8116 action_mode, action_arg_number);
8118 DrawGameValue_Time(TimeLeft);
8126 local_player->score =
8127 getModifiedActionNumber(local_player->score, 0, 99999,
8128 action_mode, action_arg_number);
8130 DrawGameValue_Score(local_player->score);
8135 case CA_SET_CE_SCORE:
8137 printf("::: CA_SET_CE_SCORE -- not yet implemented\n");
8142 case CA_SET_CE_COUNT:
8144 printf("::: CA_SET_CE_COUNT -- not yet implemented\n");
8149 case CA_SET_DYNABOMB_NUMBER:
8151 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
8156 case CA_SET_DYNABOMB_SIZE:
8158 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
8163 case CA_SET_DYNABOMB_POWER:
8165 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
8170 case CA_TOGGLE_PLAYER_GRAVITY:
8172 game.gravity = !game.gravity;
8177 case CA_ENABLE_PLAYER_GRAVITY:
8179 game.gravity = TRUE;
8184 case CA_DISABLE_PLAYER_GRAVITY:
8186 game.gravity = FALSE;
8196 static void ChangeElementNowExt(struct ElementChangeInfo *change,
8197 int x, int y, int target_element)
8199 int previous_move_direction = MovDir[x][y];
8201 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
8202 IS_WALKABLE(Feld[x][y]));
8204 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
8205 IS_WALKABLE(Feld[x][y]) &&
8209 /* check if element under player changes from accessible to unaccessible
8210 (needed for special case of dropping element which then changes) */
8211 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8212 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
8215 printf("::: BOOOM! [%d, '%s']\n", target_element,
8216 element_info[target_element].token_name);
8228 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8229 RemoveMovingField(x, y);
8233 Feld[x][y] = target_element;
8236 Feld[x][y] = target_element;
8239 ResetGfxAnimation(x, y);
8240 ResetRandomAnimationValue(x, y);
8242 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8243 MovDir[x][y] = previous_move_direction;
8246 InitField_WithBug1(x, y, FALSE);
8248 InitField(x, y, FALSE);
8249 if (CAN_MOVE(Feld[x][y]))
8253 DrawLevelField(x, y);
8255 if (GFX_CRUMBLED(Feld[x][y]))
8256 DrawLevelFieldCrumbledSandNeighbours(x, y);
8260 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
8264 TestIfBadThingTouchesHero(x, y);
8265 TestIfPlayerTouchesCustomElement(x, y);
8266 TestIfElementTouchesCustomElement(x, y);
8269 /* "Changed[][]" not set yet to allow "entered by player" change one time */
8270 if (ELEM_IS_PLAYER(target_element))
8271 RelocatePlayer(x, y, target_element);
8274 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
8276 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
8280 TestIfBadThingTouchesHero(x, y);
8281 TestIfPlayerTouchesCustomElement(x, y);
8282 TestIfElementTouchesCustomElement(x, y);
8286 if (change->has_action)
8287 ExecuteCustomElementAction(...);
8291 static boolean ChangeElementNow(int x, int y, int element, int page)
8293 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8295 int old_element = Feld[x][y];
8297 /* always use default change event to prevent running into a loop */
8298 if (ChangeEvent[x][y] == -1)
8299 ChangeEvent[x][y] = CE_DELAY;
8301 if (ChangeEvent[x][y] == CE_DELAY)
8303 /* reset actual trigger element, trigger player and action element */
8304 change->actual_trigger_element = EL_EMPTY;
8305 change->actual_trigger_player = EL_PLAYER_1;
8309 /* do not change any elements that have already changed in this frame */
8313 /* do not change already changed elements with same change event */
8314 if (Changed[x][y] & ChangeEvent[x][y])
8319 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
8321 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
8325 /* !!! indirect change before direct change !!! */
8326 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
8329 if (change->explode)
8336 if (change->use_target_content)
8338 boolean complete_replace = TRUE;
8339 boolean can_replace[3][3];
8342 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8345 boolean is_walkable;
8346 boolean is_diggable;
8347 boolean is_collectible;
8348 boolean is_removable;
8349 boolean is_destructible;
8350 int ex = x + xx - 1;
8351 int ey = y + yy - 1;
8352 int content_element = change->target_content[xx][yy];
8355 can_replace[xx][yy] = TRUE;
8357 if (ex == x && ey == y) /* do not check changing element itself */
8360 if (content_element == EL_EMPTY_SPACE)
8362 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8367 if (!IN_LEV_FIELD(ex, ey))
8369 can_replace[xx][yy] = FALSE;
8370 complete_replace = FALSE;
8376 if (Changed[ex][ey]) /* do not change already changed elements */
8378 can_replace[xx][yy] = FALSE;
8379 complete_replace = FALSE;
8387 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8388 e = MovingOrBlocked2Element(ex, ey);
8393 is_empty = (IS_FREE(ex, ey) ||
8394 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8395 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8396 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8400 is_empty = (IS_FREE(ex, ey) ||
8401 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8403 is_empty = (IS_FREE(ex, ey) ||
8404 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8409 is_walkable = (is_empty || IS_WALKABLE(e));
8410 is_diggable = (is_empty || IS_DIGGABLE(e));
8411 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8412 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8413 is_removable = (is_diggable || is_collectible);
8415 can_replace[xx][yy] =
8416 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8417 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8418 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8419 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8420 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8421 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8422 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8424 if (!can_replace[xx][yy])
8425 complete_replace = FALSE;
8427 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8428 IS_WALKABLE(content_element)));
8430 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8432 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8435 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8436 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8437 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8439 can_replace[xx][yy] = FALSE;
8440 complete_replace = FALSE;
8445 if (!change->only_if_complete || complete_replace)
8447 boolean something_has_changed = FALSE;
8449 if (change->only_if_complete && change->use_random_replace &&
8450 RND(100) < change->random_percentage)
8453 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8455 int ex = x + xx - 1;
8456 int ey = y + yy - 1;
8457 int content_element;
8459 if (can_replace[xx][yy] && (!change->use_random_replace ||
8460 RND(100) < change->random_percentage))
8462 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8463 RemoveMovingField(ex, ey);
8465 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8467 content_element = change->target_content[xx][yy];
8468 target_element = GET_TARGET_ELEMENT(content_element, change);
8470 ChangeElementNowExt(change, ex, ey, target_element);
8472 something_has_changed = TRUE;
8474 /* for symmetry reasons, freeze newly created border elements */
8475 if (ex != x || ey != y)
8476 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8480 if (something_has_changed)
8481 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8486 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8488 ChangeElementNowExt(change, x, y, target_element);
8490 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8494 /* this uses direct change before indirect change */
8495 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8501 static void ChangeElement(int x, int y, int page)
8503 int element = MovingOrBlocked2Element(x, y);
8504 struct ElementInfo *ei = &element_info[element];
8505 struct ElementChangeInfo *change = &ei->change_page[page];
8508 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8511 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8512 x, y, element, element_info[element].token_name);
8513 printf("ChangeElement(): This should never happen!\n");
8518 /* this can happen with classic bombs on walkable, changing elements */
8519 if (!CAN_CHANGE(element))
8522 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8523 ChangeDelay[x][y] = 0;
8529 if (ChangeDelay[x][y] == 0) /* initialize element change */
8532 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8534 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8535 RND(change->delay_random * change->delay_frames)) + 1;
8538 ResetGfxAnimation(x, y);
8539 ResetRandomAnimationValue(x, y);
8541 if (change->pre_change_function)
8542 change->pre_change_function(x, y);
8545 ChangeDelay[x][y]--;
8547 if (ChangeDelay[x][y] != 0) /* continue element change */
8549 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8551 if (IS_ANIMATED(graphic))
8552 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8554 if (change->change_function)
8555 change->change_function(x, y);
8557 else /* finish element change */
8559 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8561 page = ChangePage[x][y];
8562 ChangePage[x][y] = -1;
8564 change = &ei->change_page[page];
8568 if (IS_MOVING(x, y) && !change->explode)
8570 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8573 ChangeDelay[x][y] = 1; /* try change after next move step */
8574 ChangePage[x][y] = page; /* remember page to use for change */
8580 if (change->has_action)
8581 ExecuteCustomElementAction(element, page);
8584 if (ChangeElementNow(x, y, element, page))
8586 if (change->post_change_function)
8587 change->post_change_function(x, y);
8592 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8593 int trigger_element,
8600 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8602 if (!(trigger_events[trigger_element][trigger_event]))
8605 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8607 int element = EL_CUSTOM_START + i;
8609 boolean change_found = FALSE;
8610 boolean change_element = FALSE;
8613 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8614 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8617 for (j = 0; j < element_info[element].num_change_pages; j++)
8619 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8621 if (change->can_change_or_has_action &&
8622 change->has_event[trigger_event] &&
8623 change->trigger_side & trigger_side &&
8624 change->trigger_player & trigger_player &&
8625 change->trigger_page & trigger_page_bits &&
8626 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8629 if (!(change->has_event[trigger_event]))
8630 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8631 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8635 change->actual_trigger_element = trigger_element;
8636 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8638 if (change->can_change && !change_found)
8640 change_found = TRUE;
8642 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8644 if (Feld[x][y] == element)
8646 ChangeDelay[x][y] = 1;
8647 ChangeEvent[x][y] = trigger_event;
8648 ChangeElement(x, y, j);
8653 if (change->has_action)
8654 ExecuteCustomElementAction(element, j);
8656 change_element = TRUE;
8659 change->actual_trigger_element = trigger_element;
8660 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8668 if (!change_element)
8671 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8674 if (x == lx && y == ly) /* do not change trigger element itself */
8678 if (Feld[x][y] == element)
8680 ChangeDelay[x][y] = 1;
8681 ChangeEvent[x][y] = trigger_event;
8682 ChangeElement(x, y, page);
8691 static boolean CheckElementChangeExt(int x, int y,
8693 int trigger_element,
8699 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8702 if (Feld[x][y] == EL_BLOCKED)
8704 Blocked2Moving(x, y, &x, &y);
8705 element = Feld[x][y];
8709 if (Feld[x][y] != element) /* check if element has already changed */
8712 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8713 Feld[x][y], element_info[Feld[x][y]].token_name,
8714 element, element_info[element].token_name,
8723 if (trigger_page < 0)
8725 boolean change_element = FALSE;
8728 for (i = 0; i < element_info[element].num_change_pages; i++)
8730 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8732 boolean check_trigger_element =
8733 (trigger_event == CE_TOUCHING_X ||
8734 trigger_event == CE_HITTING_X ||
8735 trigger_event == CE_HIT_BY_X);
8737 if (change->can_change &&
8738 change->has_event[trigger_event] &&
8739 change->trigger_side & trigger_side &&
8740 change->trigger_player & trigger_player
8743 (!check_trigger_element ||
8744 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8748 change_element = TRUE;
8751 change->actual_trigger_element = trigger_element;
8752 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8758 if (!change_element)
8763 struct ElementInfo *ei = &element_info[element];
8764 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8766 change->actual_trigger_element = trigger_element;
8767 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8772 /* !!! this check misses pages with same event, but different side !!! */
8774 if (trigger_page < 0)
8775 trigger_page = element_info[element].event_page_nr[trigger_event];
8777 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8781 ChangeDelay[x][y] = 1;
8782 ChangeEvent[x][y] = trigger_event;
8783 ChangeElement(x, y, trigger_page);
8788 static boolean CheckElementChangeExtTEST(int x, int y,
8790 int trigger_element,
8796 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8799 if (Feld[x][y] == EL_BLOCKED)
8801 Blocked2Moving(x, y, &x, &y);
8802 element = Feld[x][y];
8806 if (Feld[x][y] != element) /* check if element has already changed */
8809 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8810 Feld[x][y], element_info[Feld[x][y]].token_name,
8811 element, element_info[element].token_name,
8820 if (trigger_page < 0)
8822 boolean change_element = FALSE;
8825 for (i = 0; i < element_info[element].num_change_pages; i++)
8827 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8829 if (change->can_change &&
8830 change->has_event[trigger_event] &&
8831 change->trigger_side & trigger_side &&
8832 change->trigger_player & trigger_player &&
8833 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8835 change_element = TRUE;
8838 change->actual_trigger_element = trigger_element;
8839 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8845 if (!change_element)
8850 struct ElementInfo *ei = &element_info[element];
8851 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8853 change->actual_trigger_element = trigger_element;
8854 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8859 /* !!! this check misses pages with same event, but different side !!! */
8861 if (trigger_page < 0)
8862 trigger_page = element_info[element].event_page_nr[trigger_event];
8864 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8868 ChangeDelay[x][y] = 1;
8869 ChangeEvent[x][y] = trigger_event;
8870 ChangeElement(x, y, trigger_page);
8875 static void PlayPlayerSound(struct PlayerInfo *player)
8877 int jx = player->jx, jy = player->jy;
8878 int element = player->element_nr;
8879 int last_action = player->last_action_waiting;
8880 int action = player->action_waiting;
8882 if (player->is_waiting)
8884 if (action != last_action)
8885 PlayLevelSoundElementAction(jx, jy, element, action);
8887 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8891 if (action != last_action)
8892 StopSound(element_info[element].sound[last_action]);
8894 if (last_action == ACTION_SLEEPING)
8895 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8899 static void PlayAllPlayersSound()
8903 for (i = 0; i < MAX_PLAYERS; i++)
8904 if (stored_player[i].active)
8905 PlayPlayerSound(&stored_player[i]);
8908 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8910 boolean last_waiting = player->is_waiting;
8911 int move_dir = player->MovDir;
8913 player->last_action_waiting = player->action_waiting;
8917 if (!last_waiting) /* not waiting -> waiting */
8919 player->is_waiting = TRUE;
8921 player->frame_counter_bored =
8923 game.player_boring_delay_fixed +
8924 SimpleRND(game.player_boring_delay_random);
8925 player->frame_counter_sleeping =
8927 game.player_sleeping_delay_fixed +
8928 SimpleRND(game.player_sleeping_delay_random);
8930 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8933 if (game.player_sleeping_delay_fixed +
8934 game.player_sleeping_delay_random > 0 &&
8935 player->anim_delay_counter == 0 &&
8936 player->post_delay_counter == 0 &&
8937 FrameCounter >= player->frame_counter_sleeping)
8938 player->is_sleeping = TRUE;
8939 else if (game.player_boring_delay_fixed +
8940 game.player_boring_delay_random > 0 &&
8941 FrameCounter >= player->frame_counter_bored)
8942 player->is_bored = TRUE;
8944 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8945 player->is_bored ? ACTION_BORING :
8948 if (player->is_sleeping)
8950 if (player->num_special_action_sleeping > 0)
8952 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8954 int last_special_action = player->special_action_sleeping;
8955 int num_special_action = player->num_special_action_sleeping;
8956 int special_action =
8957 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8958 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8959 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8960 last_special_action + 1 : ACTION_SLEEPING);
8961 int special_graphic =
8962 el_act_dir2img(player->element_nr, special_action, move_dir);
8964 player->anim_delay_counter =
8965 graphic_info[special_graphic].anim_delay_fixed +
8966 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8967 player->post_delay_counter =
8968 graphic_info[special_graphic].post_delay_fixed +
8969 SimpleRND(graphic_info[special_graphic].post_delay_random);
8971 player->special_action_sleeping = special_action;
8974 if (player->anim_delay_counter > 0)
8976 player->action_waiting = player->special_action_sleeping;
8977 player->anim_delay_counter--;
8979 else if (player->post_delay_counter > 0)
8981 player->post_delay_counter--;
8985 else if (player->is_bored)
8987 if (player->num_special_action_bored > 0)
8989 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8991 int special_action =
8992 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8993 int special_graphic =
8994 el_act_dir2img(player->element_nr, special_action, move_dir);
8996 player->anim_delay_counter =
8997 graphic_info[special_graphic].anim_delay_fixed +
8998 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8999 player->post_delay_counter =
9000 graphic_info[special_graphic].post_delay_fixed +
9001 SimpleRND(graphic_info[special_graphic].post_delay_random);
9003 player->special_action_bored = special_action;
9006 if (player->anim_delay_counter > 0)
9008 player->action_waiting = player->special_action_bored;
9009 player->anim_delay_counter--;
9011 else if (player->post_delay_counter > 0)
9013 player->post_delay_counter--;
9018 else if (last_waiting) /* waiting -> not waiting */
9020 player->is_waiting = FALSE;
9021 player->is_bored = FALSE;
9022 player->is_sleeping = FALSE;
9024 player->frame_counter_bored = -1;
9025 player->frame_counter_sleeping = -1;
9027 player->anim_delay_counter = 0;
9028 player->post_delay_counter = 0;
9030 player->action_waiting = ACTION_DEFAULT;
9032 player->special_action_bored = ACTION_DEFAULT;
9033 player->special_action_sleeping = ACTION_DEFAULT;
9038 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9041 static byte stored_player_action[MAX_PLAYERS];
9042 static int num_stored_actions = 0;
9044 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9045 int left = player_action & JOY_LEFT;
9046 int right = player_action & JOY_RIGHT;
9047 int up = player_action & JOY_UP;
9048 int down = player_action & JOY_DOWN;
9049 int button1 = player_action & JOY_BUTTON_1;
9050 int button2 = player_action & JOY_BUTTON_2;
9051 int dx = (left ? -1 : right ? 1 : 0);
9052 int dy = (up ? -1 : down ? 1 : 0);
9055 stored_player_action[player->index_nr] = 0;
9056 num_stored_actions++;
9060 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
9063 if (!player->active || tape.pausing)
9067 printf("::: [%d %d %d %d] [%d %d]\n",
9068 left, right, up, down, button1, button2);
9074 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
9079 if (player->MovPos == 0)
9080 CheckGravityMovement(player);
9083 snapped = SnapField(player, dx, dy);
9087 dropped = DropElement(player);
9089 moved = MovePlayer(player, dx, dy);
9092 if (tape.single_step && tape.recording && !tape.pausing)
9094 if (button1 || (dropped && !moved))
9096 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9097 SnapField(player, 0, 0); /* stop snapping */
9101 SetPlayerWaiting(player, FALSE);
9104 return player_action;
9106 stored_player_action[player->index_nr] = player_action;
9112 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
9115 /* no actions for this player (no input at player's configured device) */
9117 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9118 SnapField(player, 0, 0);
9119 CheckGravityMovementWhenNotMoving(player);
9121 if (player->MovPos == 0)
9122 SetPlayerWaiting(player, TRUE);
9124 if (player->MovPos == 0) /* needed for tape.playing */
9125 player->is_moving = FALSE;
9127 player->is_dropping = FALSE;
9133 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
9135 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
9137 TapeRecordAction(stored_player_action);
9138 num_stored_actions = 0;
9145 static void PlayerActions(struct PlayerInfo *player, byte player_action)
9147 static byte stored_player_action[MAX_PLAYERS];
9148 static int num_stored_actions = 0;
9149 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9150 int left = player_action & JOY_LEFT;
9151 int right = player_action & JOY_RIGHT;
9152 int up = player_action & JOY_UP;
9153 int down = player_action & JOY_DOWN;
9154 int button1 = player_action & JOY_BUTTON_1;
9155 int button2 = player_action & JOY_BUTTON_2;
9156 int dx = (left ? -1 : right ? 1 : 0);
9157 int dy = (up ? -1 : down ? 1 : 0);
9159 stored_player_action[player->index_nr] = 0;
9160 num_stored_actions++;
9162 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
9164 if (!player->active || tape.pausing)
9169 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
9172 snapped = SnapField(player, dx, dy);
9176 dropped = DropElement(player);
9178 moved = MovePlayer(player, dx, dy);
9181 if (tape.single_step && tape.recording && !tape.pausing)
9183 if (button1 || (dropped && !moved))
9185 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9186 SnapField(player, 0, 0); /* stop snapping */
9190 stored_player_action[player->index_nr] = player_action;
9194 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
9196 /* no actions for this player (no input at player's configured device) */
9198 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9199 SnapField(player, 0, 0);
9200 CheckGravityMovementWhenNotMoving(player);
9202 if (player->MovPos == 0)
9203 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
9205 if (player->MovPos == 0) /* needed for tape.playing */
9206 player->is_moving = FALSE;
9209 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
9211 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
9213 TapeRecordAction(stored_player_action);
9214 num_stored_actions = 0;
9219 void AdvanceFrameAndPlayerCounters(int player_nr)
9223 /* advance frame counters (global frame counter and time frame counter) */
9227 /* advance player counters (counters for move delay, move animation etc.) */
9228 for (i = 0; i < MAX_PLAYERS; i++)
9230 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9232 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9234 if (!advance_player_counters) /* not all players may be affected */
9237 stored_player[i].Frame += move_frames;
9239 if (stored_player[i].MovPos != 0)
9240 stored_player[i].StepFrame += move_frames;
9242 #if USE_NEW_MOVE_DELAY
9243 if (stored_player[i].move_delay > 0)
9244 stored_player[i].move_delay--;
9247 #if USE_NEW_PUSH_DELAY
9248 /* due to bugs in previous versions, counter must count up, not down */
9249 if (stored_player[i].push_delay != -1)
9250 stored_player[i].push_delay++;
9253 if (stored_player[i].drop_delay > 0)
9254 stored_player[i].drop_delay--;
9260 static unsigned long game_frame_delay = 0;
9261 unsigned long game_frame_delay_value;
9262 int magic_wall_x = 0, magic_wall_y = 0;
9263 int i, x, y, element, graphic;
9264 byte *recorded_player_action;
9265 byte summarized_player_action = 0;
9267 byte tape_action[MAX_PLAYERS];
9270 if (game_status != GAME_MODE_PLAYING)
9273 game_frame_delay_value =
9274 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9276 if (tape.playing && tape.warp_forward && !tape.pausing)
9277 game_frame_delay_value = 0;
9279 /* ---------- main game synchronization point ---------- */
9281 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9283 if (network_playing && !network_player_action_received)
9287 printf("DEBUG: try to get network player actions in time\n");
9291 #if defined(NETWORK_AVALIABLE)
9292 /* last chance to get network player actions without main loop delay */
9296 if (game_status != GAME_MODE_PLAYING)
9299 if (!network_player_action_received)
9303 printf("DEBUG: failed to get network player actions in time\n");
9314 printf("::: getting new tape action [%d]\n", FrameCounter);
9317 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9320 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
9321 if (recorded_player_action == NULL && tape.pausing)
9326 printf("::: %d\n", stored_player[0].action);
9330 if (recorded_player_action != NULL)
9331 for (i = 0; i < MAX_PLAYERS; i++)
9332 stored_player[i].action = recorded_player_action[i];
9335 for (i = 0; i < MAX_PLAYERS; i++)
9337 summarized_player_action |= stored_player[i].action;
9339 if (!network_playing)
9340 stored_player[i].effective_action = stored_player[i].action;
9343 #if defined(NETWORK_AVALIABLE)
9344 if (network_playing)
9345 SendToServer_MovePlayer(summarized_player_action);
9348 if (!options.network && !setup.team_mode)
9349 local_player->effective_action = summarized_player_action;
9352 if (recorded_player_action != NULL)
9353 for (i = 0; i < MAX_PLAYERS; i++)
9354 stored_player[i].effective_action = recorded_player_action[i];
9358 for (i = 0; i < MAX_PLAYERS; i++)
9360 tape_action[i] = stored_player[i].effective_action;
9362 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9363 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9366 /* only save actions from input devices, but not programmed actions */
9368 TapeRecordAction(tape_action);
9371 for (i = 0; i < MAX_PLAYERS; i++)
9373 int actual_player_action = stored_player[i].effective_action;
9376 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9377 - rnd_equinox_tetrachloride 048
9378 - rnd_equinox_tetrachloride_ii 096
9379 - rnd_emanuel_schmieg 002
9380 - doctor_sloan_ww 001, 020
9382 if (stored_player[i].MovPos == 0)
9383 CheckGravityMovement(&stored_player[i]);
9387 /* overwrite programmed action with tape action */
9388 if (stored_player[i].programmed_action)
9389 actual_player_action = stored_player[i].programmed_action;
9393 if (stored_player[i].programmed_action)
9394 printf("::: %d\n", stored_player[i].programmed_action);
9397 if (recorded_player_action)
9400 if (stored_player[i].programmed_action &&
9401 stored_player[i].programmed_action != recorded_player_action[i])
9402 printf("::: %d: %d <-> %d\n", i,
9403 stored_player[i].programmed_action, recorded_player_action[i]);
9407 actual_player_action = recorded_player_action[i];
9412 /* overwrite tape action with programmed action */
9413 if (stored_player[i].programmed_action)
9414 actual_player_action = stored_player[i].programmed_action;
9419 printf("::: action: %d: %x [%d]\n",
9420 stored_player[i].MovPos, actual_player_action, FrameCounter);
9424 PlayerActions(&stored_player[i], actual_player_action);
9426 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9428 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9429 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9432 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9437 TapeRecordAction(tape_action);
9440 network_player_action_received = FALSE;
9442 ScrollScreen(NULL, SCROLL_GO_ON);
9448 for (i = 0; i < MAX_PLAYERS; i++)
9449 stored_player[i].Frame++;
9453 /* for backwards compatibility, the following code emulates a fixed bug that
9454 occured when pushing elements (causing elements that just made their last
9455 pushing step to already (if possible) make their first falling step in the
9456 same game frame, which is bad); this code is also needed to use the famous
9457 "spring push bug" which is used in older levels and might be wanted to be
9458 used also in newer levels, but in this case the buggy pushing code is only
9459 affecting the "spring" element and no other elements */
9462 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9464 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9467 for (i = 0; i < MAX_PLAYERS; i++)
9469 struct PlayerInfo *player = &stored_player[i];
9474 if (player->active && player->is_pushing && player->is_moving &&
9476 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9477 Feld[x][y] == EL_SPRING))
9479 if (player->active && player->is_pushing && player->is_moving &&
9483 ContinueMoving(x, y);
9485 /* continue moving after pushing (this is actually a bug) */
9486 if (!IS_MOVING(x, y))
9495 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9497 Changed[x][y] = FALSE;
9498 ChangeEvent[x][y] = -1;
9500 #if USE_NEW_BLOCK_STYLE
9501 /* this must be handled before main playfield loop */
9502 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9505 if (MovDelay[x][y] <= 0)
9511 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9513 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9514 printf("GameActions(): This should never happen!\n");
9516 ChangePage[x][y] = -1;
9521 if (WasJustMoving[x][y] > 0)
9522 WasJustMoving[x][y]--;
9523 if (WasJustFalling[x][y] > 0)
9524 WasJustFalling[x][y]--;
9525 if (CheckCollision[x][y] > 0)
9526 CheckCollision[x][y]--;
9531 /* reset finished pushing action (not done in ContinueMoving() to allow
9532 continous pushing animation for elements with zero push delay) */
9533 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9535 ResetGfxAnimation(x, y);
9536 DrawLevelField(x, y);
9541 if (IS_BLOCKED(x, y))
9545 Blocked2Moving(x, y, &oldx, &oldy);
9546 if (!IS_MOVING(oldx, oldy))
9548 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9549 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9550 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9551 printf("GameActions(): This should never happen!\n");
9557 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9559 element = Feld[x][y];
9561 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9563 graphic = el2img(element);
9569 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9571 element = graphic = 0;
9575 if (graphic_info[graphic].anim_global_sync)
9576 GfxFrame[x][y] = FrameCounter;
9578 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9579 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9580 ResetRandomAnimationValue(x, y);
9582 SetRandomAnimationValue(x, y);
9585 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9588 if (IS_INACTIVE(element))
9590 if (IS_ANIMATED(graphic))
9591 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9597 /* this may take place after moving, so 'element' may have changed */
9599 if (IS_CHANGING(x, y))
9601 if (IS_CHANGING(x, y) &&
9602 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9606 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9607 element_info[element].event_page_nr[CE_DELAY]);
9609 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9612 element = Feld[x][y];
9613 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9617 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9622 element = Feld[x][y];
9623 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9625 if (element == EL_MOLE)
9626 printf("::: %d, %d, %d [%d]\n",
9627 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9631 if (element == EL_YAMYAM)
9632 printf("::: %d, %d, %d\n",
9633 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9637 if (IS_ANIMATED(graphic) &&
9641 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9644 if (element == EL_BUG)
9645 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9649 if (element == EL_MOLE)
9650 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9654 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9655 EdelsteinFunkeln(x, y);
9657 else if ((element == EL_ACID ||
9658 element == EL_EXIT_OPEN ||
9659 element == EL_SP_EXIT_OPEN ||
9660 element == EL_SP_TERMINAL ||
9661 element == EL_SP_TERMINAL_ACTIVE ||
9662 element == EL_EXTRA_TIME ||
9663 element == EL_SHIELD_NORMAL ||
9664 element == EL_SHIELD_DEADLY) &&
9665 IS_ANIMATED(graphic))
9666 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9667 else if (IS_MOVING(x, y))
9668 ContinueMoving(x, y);
9669 else if (IS_ACTIVE_BOMB(element))
9670 CheckDynamite(x, y);
9672 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9673 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9675 else if (element == EL_AMOEBA_GROWING)
9676 AmoebeWaechst(x, y);
9677 else if (element == EL_AMOEBA_SHRINKING)
9678 AmoebaDisappearing(x, y);
9680 #if !USE_NEW_AMOEBA_CODE
9681 else if (IS_AMOEBALIVE(element))
9682 AmoebeAbleger(x, y);
9685 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9687 else if (element == EL_EXIT_CLOSED)
9689 else if (element == EL_SP_EXIT_CLOSED)
9691 else if (element == EL_EXPANDABLE_WALL_GROWING)
9693 else if (element == EL_EXPANDABLE_WALL ||
9694 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9695 element == EL_EXPANDABLE_WALL_VERTICAL ||
9696 element == EL_EXPANDABLE_WALL_ANY)
9698 else if (element == EL_FLAMES)
9699 CheckForDragon(x, y);
9701 else if (IS_AUTO_CHANGING(element))
9702 ChangeElement(x, y);
9704 else if (element == EL_EXPLOSION)
9705 ; /* drawing of correct explosion animation is handled separately */
9706 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9707 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9710 /* this may take place after moving, so 'element' may have changed */
9711 if (IS_AUTO_CHANGING(Feld[x][y]))
9712 ChangeElement(x, y);
9715 if (IS_BELT_ACTIVE(element))
9716 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9718 if (game.magic_wall_active)
9720 int jx = local_player->jx, jy = local_player->jy;
9722 /* play the element sound at the position nearest to the player */
9723 if ((element == EL_MAGIC_WALL_FULL ||
9724 element == EL_MAGIC_WALL_ACTIVE ||
9725 element == EL_MAGIC_WALL_EMPTYING ||
9726 element == EL_BD_MAGIC_WALL_FULL ||
9727 element == EL_BD_MAGIC_WALL_ACTIVE ||
9728 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9729 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9737 #if USE_NEW_AMOEBA_CODE
9738 /* new experimental amoeba growth stuff */
9740 if (!(FrameCounter % 8))
9743 static unsigned long random = 1684108901;
9745 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9748 x = (random >> 10) % lev_fieldx;
9749 y = (random >> 20) % lev_fieldy;
9751 x = RND(lev_fieldx);
9752 y = RND(lev_fieldy);
9754 element = Feld[x][y];
9757 if (!IS_PLAYER(x,y) &&
9758 (element == EL_EMPTY ||
9759 CAN_GROW_INTO(element) ||
9760 element == EL_QUICKSAND_EMPTY ||
9761 element == EL_ACID_SPLASH_LEFT ||
9762 element == EL_ACID_SPLASH_RIGHT))
9764 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9765 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9766 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9767 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9768 Feld[x][y] = EL_AMOEBA_DROP;
9771 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9772 if (!IS_PLAYER(x,y) &&
9773 (element == EL_EMPTY ||
9774 element == EL_SAND ||
9775 element == EL_QUICKSAND_EMPTY ||
9776 element == EL_ACID_SPLASH_LEFT ||
9777 element == EL_ACID_SPLASH_RIGHT))
9779 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9780 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9781 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9782 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9783 Feld[x][y] = EL_AMOEBA_DROP;
9787 random = random * 129 + 1;
9793 if (game.explosions_delayed)
9796 game.explosions_delayed = FALSE;
9798 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9800 element = Feld[x][y];
9802 if (ExplodeField[x][y])
9803 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9804 else if (element == EL_EXPLOSION)
9805 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9807 ExplodeField[x][y] = EX_TYPE_NONE;
9810 game.explosions_delayed = TRUE;
9813 if (game.magic_wall_active)
9815 if (!(game.magic_wall_time_left % 4))
9817 int element = Feld[magic_wall_x][magic_wall_y];
9819 if (element == EL_BD_MAGIC_WALL_FULL ||
9820 element == EL_BD_MAGIC_WALL_ACTIVE ||
9821 element == EL_BD_MAGIC_WALL_EMPTYING)
9822 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9824 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9827 if (game.magic_wall_time_left > 0)
9829 game.magic_wall_time_left--;
9830 if (!game.magic_wall_time_left)
9832 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9834 element = Feld[x][y];
9836 if (element == EL_MAGIC_WALL_ACTIVE ||
9837 element == EL_MAGIC_WALL_FULL)
9839 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9840 DrawLevelField(x, y);
9842 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9843 element == EL_BD_MAGIC_WALL_FULL)
9845 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9846 DrawLevelField(x, y);
9850 game.magic_wall_active = FALSE;
9855 if (game.light_time_left > 0)
9857 game.light_time_left--;
9859 if (game.light_time_left == 0)
9860 RedrawAllLightSwitchesAndInvisibleElements();
9863 if (game.timegate_time_left > 0)
9865 game.timegate_time_left--;
9867 if (game.timegate_time_left == 0)
9868 CloseAllOpenTimegates();
9871 for (i = 0; i < MAX_PLAYERS; i++)
9873 struct PlayerInfo *player = &stored_player[i];
9875 if (SHIELD_ON(player))
9877 if (player->shield_deadly_time_left)
9878 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9879 else if (player->shield_normal_time_left)
9880 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9884 if (TimeFrames >= FRAMES_PER_SECOND)
9889 for (i = 0; i < MAX_PLAYERS; i++)
9891 struct PlayerInfo *player = &stored_player[i];
9893 if (SHIELD_ON(player))
9895 player->shield_normal_time_left--;
9897 if (player->shield_deadly_time_left > 0)
9898 player->shield_deadly_time_left--;
9902 if (!level.use_step_counter)
9910 if (TimeLeft <= 10 && setup.time_limit)
9911 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9913 DrawGameValue_Time(TimeLeft);
9915 if (!TimeLeft && setup.time_limit)
9916 for (i = 0; i < MAX_PLAYERS; i++)
9917 KillHero(&stored_player[i]);
9919 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9920 DrawGameValue_Time(TimePlayed);
9923 if (tape.recording || tape.playing)
9924 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9928 PlayAllPlayersSound();
9930 if (options.debug) /* calculate frames per second */
9932 static unsigned long fps_counter = 0;
9933 static int fps_frames = 0;
9934 unsigned long fps_delay_ms = Counter() - fps_counter;
9938 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9940 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9943 fps_counter = Counter();
9946 redraw_mask |= REDRAW_FPS;
9950 if (stored_player[0].jx != stored_player[0].last_jx ||
9951 stored_player[0].jy != stored_player[0].last_jy)
9952 printf("::: %d, %d, %d, %d, %d\n",
9953 stored_player[0].MovDir,
9954 stored_player[0].MovPos,
9955 stored_player[0].GfxPos,
9956 stored_player[0].Frame,
9957 stored_player[0].StepFrame);
9960 #if USE_NEW_MOVE_DELAY
9961 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9966 for (i = 0; i < MAX_PLAYERS; i++)
9969 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9971 stored_player[i].Frame += move_frames;
9973 if (stored_player[i].MovPos != 0)
9974 stored_player[i].StepFrame += move_frames;
9976 #if USE_NEW_MOVE_DELAY
9977 if (stored_player[i].move_delay > 0)
9978 stored_player[i].move_delay--;
9981 if (stored_player[i].drop_delay > 0)
9982 stored_player[i].drop_delay--;
9987 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9989 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9991 local_player->show_envelope = 0;
9995 #if USE_NEW_RANDOMIZE
9996 /* use random number generator in every frame to make it less predictable */
9997 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10002 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10004 int min_x = x, min_y = y, max_x = x, max_y = y;
10007 for (i = 0; i < MAX_PLAYERS; i++)
10009 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10011 if (!stored_player[i].active || &stored_player[i] == player)
10014 min_x = MIN(min_x, jx);
10015 min_y = MIN(min_y, jy);
10016 max_x = MAX(max_x, jx);
10017 max_y = MAX(max_y, jy);
10020 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10023 static boolean AllPlayersInVisibleScreen()
10027 for (i = 0; i < MAX_PLAYERS; i++)
10029 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10031 if (!stored_player[i].active)
10034 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10041 void ScrollLevel(int dx, int dy)
10043 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10046 BlitBitmap(drawto_field, drawto_field,
10047 FX + TILEX * (dx == -1) - softscroll_offset,
10048 FY + TILEY * (dy == -1) - softscroll_offset,
10049 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10050 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10051 FX + TILEX * (dx == 1) - softscroll_offset,
10052 FY + TILEY * (dy == 1) - softscroll_offset);
10056 x = (dx == 1 ? BX1 : BX2);
10057 for (y = BY1; y <= BY2; y++)
10058 DrawScreenField(x, y);
10063 y = (dy == 1 ? BY1 : BY2);
10064 for (x = BX1; x <= BX2; x++)
10065 DrawScreenField(x, y);
10068 redraw_mask |= REDRAW_FIELD;
10072 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
10074 int nextx = x + dx, nexty = y + dy;
10075 int element = Feld[x][y];
10078 element != EL_SP_PORT_LEFT &&
10079 element != EL_SP_GRAVITY_PORT_LEFT &&
10080 element != EL_SP_PORT_HORIZONTAL &&
10081 element != EL_SP_PORT_ANY) ||
10083 element != EL_SP_PORT_RIGHT &&
10084 element != EL_SP_GRAVITY_PORT_RIGHT &&
10085 element != EL_SP_PORT_HORIZONTAL &&
10086 element != EL_SP_PORT_ANY) ||
10088 element != EL_SP_PORT_UP &&
10089 element != EL_SP_GRAVITY_PORT_UP &&
10090 element != EL_SP_PORT_VERTICAL &&
10091 element != EL_SP_PORT_ANY) ||
10093 element != EL_SP_PORT_DOWN &&
10094 element != EL_SP_GRAVITY_PORT_DOWN &&
10095 element != EL_SP_PORT_VERTICAL &&
10096 element != EL_SP_PORT_ANY) ||
10097 !IN_LEV_FIELD(nextx, nexty) ||
10098 !IS_FREE(nextx, nexty))
10105 static boolean canFallDown(struct PlayerInfo *player)
10107 int jx = player->jx, jy = player->jy;
10109 return (IN_LEV_FIELD(jx, jy + 1) &&
10110 (IS_FREE(jx, jy + 1) ||
10111 #if USE_NEW_BLOCK_STYLE
10112 #if USE_GRAVITY_BUGFIX_OLD
10113 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
10116 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10117 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10118 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10121 static boolean canPassField(int x, int y, int move_dir)
10123 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10124 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10125 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10126 int nextx = x + dx;
10127 int nexty = y + dy;
10128 int element = Feld[x][y];
10130 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10131 !CAN_MOVE(element) &&
10132 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10133 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10134 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10137 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10139 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10140 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10141 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10145 int nextx = newx + dx;
10146 int nexty = newy + dy;
10150 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10151 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10153 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
10155 (IS_DIGGABLE(Feld[newx][newy]) ||
10156 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10157 canPassField(newx, newy, move_dir)));
10160 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10161 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10162 (IS_DIGGABLE(Feld[newx][newy]) ||
10163 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10164 canPassField(newx, newy, move_dir)));
10167 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10168 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
10169 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10170 canPassField(newx, newy, move_dir)));
10172 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10173 (IS_DIGGABLE(Feld[newx][newy]) ||
10174 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10175 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
10176 !CAN_MOVE(Feld[newx][newy]) &&
10177 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10178 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10179 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
10185 static void CheckGravityMovement(struct PlayerInfo *player)
10187 if (game.gravity && !player->programmed_action)
10190 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10191 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10193 int move_dir_horizontal = player->action & MV_HORIZONTAL;
10194 int move_dir_vertical = player->action & MV_VERTICAL;
10198 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10200 boolean player_is_snapping = player->action & JOY_BUTTON_1;
10203 int jx = player->jx, jy = player->jy;
10205 boolean player_is_moving_to_valid_field =
10206 (!player_is_snapping &&
10207 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10208 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10212 (player->last_move_dir & MV_HORIZONTAL ?
10213 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
10214 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
10218 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10219 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10220 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10221 int new_jx = jx + dx, new_jy = jy + dy;
10222 int nextx = new_jx + dx, nexty = new_jy + dy;
10228 boolean player_can_fall_down = canFallDown(player);
10230 boolean player_can_fall_down =
10231 (IN_LEV_FIELD(jx, jy + 1) &&
10232 (IS_FREE(jx, jy + 1) ||
10233 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
10237 boolean player_can_fall_down =
10238 (IN_LEV_FIELD(jx, jy + 1) &&
10239 (IS_FREE(jx, jy + 1)));
10243 boolean player_is_moving_to_valid_field =
10246 !player_is_snapping &&
10250 IN_LEV_FIELD(new_jx, new_jy) &&
10251 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
10252 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
10253 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
10254 IN_LEV_FIELD(nextx, nexty) &&
10255 element_info[Feld[nextx][nexty]].access_direction & move_dir))
10257 IN_LEV_FIELD(new_jx, new_jy) &&
10258 (Feld[new_jx][new_jy] == EL_SP_BASE ||
10259 Feld[new_jx][new_jy] == EL_SAND ||
10260 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
10261 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
10262 /* !!! extend EL_SAND to anything diggable !!! */
10268 boolean player_is_standing_on_valid_field =
10269 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10270 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
10274 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
10275 player_can_fall_down,
10276 player_is_standing_on_valid_field,
10277 player_is_moving_to_valid_field,
10278 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
10279 player->effective_action,
10280 player->can_fall_into_acid);
10283 if (player_can_fall_down &&
10285 !player_is_standing_on_valid_field &&
10287 !player_is_moving_to_valid_field)
10290 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
10291 jx, jy, FrameCounter);
10294 player->programmed_action = MV_DOWN;
10299 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10302 return CheckGravityMovement(player);
10305 if (game.gravity && !player->programmed_action)
10307 int jx = player->jx, jy = player->jy;
10308 boolean field_under_player_is_free =
10309 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10310 boolean player_is_standing_on_valid_field =
10311 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10312 (IS_WALKABLE(Feld[jx][jy]) &&
10313 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10315 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10316 player->programmed_action = MV_DOWN;
10321 MovePlayerOneStep()
10322 -----------------------------------------------------------------------------
10323 dx, dy: direction (non-diagonal) to try to move the player to
10324 real_dx, real_dy: direction as read from input device (can be diagonal)
10327 boolean MovePlayerOneStep(struct PlayerInfo *player,
10328 int dx, int dy, int real_dx, int real_dy)
10331 static int trigger_sides[4][2] =
10333 /* enter side leave side */
10334 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10335 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10336 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10337 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10339 int move_direction = (dx == -1 ? MV_LEFT :
10340 dx == +1 ? MV_RIGHT :
10342 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10343 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10344 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10346 int jx = player->jx, jy = player->jy;
10347 int new_jx = jx + dx, new_jy = jy + dy;
10351 if (!player->active || (!dx && !dy))
10352 return MF_NO_ACTION;
10354 player->MovDir = (dx < 0 ? MV_LEFT :
10355 dx > 0 ? MV_RIGHT :
10357 dy > 0 ? MV_DOWN : MV_NO_MOVING);
10359 if (!IN_LEV_FIELD(new_jx, new_jy))
10360 return MF_NO_ACTION;
10362 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10363 return MF_NO_ACTION;
10366 element = MovingOrBlocked2Element(new_jx, new_jy);
10368 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10371 if (DONT_RUN_INTO(element))
10373 if (element == EL_ACID && dx == 0 && dy == 1)
10375 SplashAcid(new_jx, new_jy);
10376 Feld[jx][jy] = EL_PLAYER_1;
10377 InitMovingField(jx, jy, MV_DOWN);
10378 Store[jx][jy] = EL_ACID;
10379 ContinueMoving(jx, jy);
10383 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
10388 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10389 if (can_move != MF_MOVING)
10392 /* check if DigField() has caused relocation of the player */
10393 if (player->jx != jx || player->jy != jy)
10394 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
10396 StorePlayer[jx][jy] = 0;
10397 player->last_jx = jx;
10398 player->last_jy = jy;
10399 player->jx = new_jx;
10400 player->jy = new_jy;
10401 StorePlayer[new_jx][new_jy] = player->element_nr;
10404 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10406 player->step_counter++;
10409 player->drop_delay = 0;
10412 PlayerVisit[jx][jy] = FrameCounter;
10414 ScrollPlayer(player, SCROLL_INIT);
10417 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
10419 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
10421 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
10424 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
10426 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
10427 CE_PLAYER_ENTERS_X, enter_side);
10428 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
10429 CE_ENTERED_BY_PLAYER, enter_side);
10436 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10438 int jx = player->jx, jy = player->jy;
10439 int old_jx = jx, old_jy = jy;
10440 int moved = MF_NO_ACTION;
10443 if (!player->active)
10448 if (player->MovPos == 0)
10450 player->is_moving = FALSE;
10451 player->is_digging = FALSE;
10452 player->is_collecting = FALSE;
10453 player->is_snapping = FALSE;
10454 player->is_pushing = FALSE;
10460 if (!player->active || (!dx && !dy))
10465 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10473 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
10474 player->move_delay + player->move_delay_value);
10477 #if USE_NEW_MOVE_DELAY
10478 if (player->move_delay > 0)
10480 if (!FrameReached(&player->move_delay, player->move_delay_value))
10484 printf("::: can NOT move\n");
10490 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10491 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10498 printf("::: COULD move now\n");
10501 #if USE_NEW_MOVE_DELAY
10502 player->move_delay = -1; /* set to "uninitialized" value */
10505 /* store if player is automatically moved to next field */
10506 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10508 /* remove the last programmed player action */
10509 player->programmed_action = 0;
10511 if (player->MovPos)
10513 /* should only happen if pre-1.2 tape recordings are played */
10514 /* this is only for backward compatibility */
10516 int original_move_delay_value = player->move_delay_value;
10519 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10523 /* scroll remaining steps with finest movement resolution */
10524 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10526 while (player->MovPos)
10528 ScrollPlayer(player, SCROLL_GO_ON);
10529 ScrollScreen(NULL, SCROLL_GO_ON);
10531 #if USE_NEW_MOVE_DELAY
10532 AdvanceFrameAndPlayerCounters(player->index_nr);
10541 player->move_delay_value = original_move_delay_value;
10544 if (player->last_move_dir & MV_HORIZONTAL)
10546 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10547 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10551 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10552 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10558 if (moved & MF_MOVING && !ScreenMovPos &&
10559 (player == local_player || !options.network))
10561 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10562 int offset = (setup.scroll_delay ? 3 : 0);
10564 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10566 /* actual player has left the screen -- scroll in that direction */
10567 if (jx != old_jx) /* player has moved horizontally */
10568 scroll_x += (jx - old_jx);
10569 else /* player has moved vertically */
10570 scroll_y += (jy - old_jy);
10574 if (jx != old_jx) /* player has moved horizontally */
10576 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10577 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10578 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10580 /* don't scroll over playfield boundaries */
10581 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10582 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10584 /* don't scroll more than one field at a time */
10585 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10587 /* don't scroll against the player's moving direction */
10588 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10589 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10590 scroll_x = old_scroll_x;
10592 else /* player has moved vertically */
10594 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10595 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10596 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10598 /* don't scroll over playfield boundaries */
10599 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10600 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10602 /* don't scroll more than one field at a time */
10603 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10605 /* don't scroll against the player's moving direction */
10606 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10607 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10608 scroll_y = old_scroll_y;
10612 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10614 if (!options.network && !AllPlayersInVisibleScreen())
10616 scroll_x = old_scroll_x;
10617 scroll_y = old_scroll_y;
10621 ScrollScreen(player, SCROLL_INIT);
10622 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10629 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10631 if (!(moved & MF_MOVING) && !player->is_pushing)
10636 player->StepFrame = 0;
10638 if (moved & MF_MOVING)
10641 printf("::: REALLY moves now\n");
10644 if (old_jx != jx && old_jy == jy)
10645 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10646 else if (old_jx == jx && old_jy != jy)
10647 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10649 DrawLevelField(jx, jy); /* for "crumbled sand" */
10651 player->last_move_dir = player->MovDir;
10652 player->is_moving = TRUE;
10654 player->is_snapping = FALSE;
10658 player->is_switching = FALSE;
10661 player->is_dropping = FALSE;
10665 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10668 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10671 int move_direction = player->MovDir;
10673 int enter_side = MV_DIR_OPPOSITE(move_direction);
10674 int leave_side = move_direction;
10676 static int trigger_sides[4][2] =
10678 /* enter side leave side */
10679 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10680 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10681 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10682 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10684 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10685 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10687 int old_element = Feld[old_jx][old_jy];
10688 int new_element = Feld[jx][jy];
10691 /* !!! TEST ONLY !!! */
10692 if (IS_CUSTOM_ELEMENT(old_element))
10693 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10695 player->index_bit, leave_side);
10697 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10698 CE_PLAYER_LEAVES_X,
10699 player->index_bit, leave_side);
10701 if (IS_CUSTOM_ELEMENT(new_element))
10702 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10703 player->index_bit, enter_side);
10705 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10706 CE_PLAYER_ENTERS_X,
10707 player->index_bit, enter_side);
10717 CheckGravityMovementWhenNotMoving(player);
10720 player->last_move_dir = MV_NO_MOVING;
10722 player->is_moving = FALSE;
10724 #if USE_NEW_MOVE_STYLE
10725 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10726 /* ensure that the player is also allowed to move in the next frame */
10727 /* (currently, the player is forced to wait eight frames before he can try
10730 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10731 player->move_delay = 0; /* allow direct movement in the next frame */
10735 #if USE_NEW_MOVE_DELAY
10736 if (player->move_delay == -1) /* not yet initialized by DigField() */
10737 player->move_delay = player->move_delay_value;
10740 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10742 TestIfHeroTouchesBadThing(jx, jy);
10743 TestIfPlayerTouchesCustomElement(jx, jy);
10746 if (!player->active)
10747 RemoveHero(player);
10752 void ScrollPlayer(struct PlayerInfo *player, int mode)
10754 int jx = player->jx, jy = player->jy;
10755 int last_jx = player->last_jx, last_jy = player->last_jy;
10756 int move_stepsize = TILEX / player->move_delay_value;
10758 if (!player->active || !player->MovPos)
10761 if (mode == SCROLL_INIT)
10763 player->actual_frame_counter = FrameCounter;
10764 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10767 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10769 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10770 player->block_delay);
10773 #if USE_NEW_BLOCK_STYLE
10776 if (player->block_delay <= 0)
10777 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10780 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10781 Feld[last_jx][last_jy] == EL_EMPTY)
10783 int last_field_block_delay = 0; /* start with no blocking at all */
10784 int block_delay_adjustment = player->block_delay_adjustment;
10786 /* if player blocks last field, add delay for exactly one move */
10787 if (player->block_last_field)
10789 last_field_block_delay += player->move_delay_value;
10791 #if USE_GRAVITY_BUGFIX_NEW
10792 /* when blocking enabled, prevent moving up despite gravity */
10793 if (game.gravity && player->MovDir == MV_UP)
10794 block_delay_adjustment = -1;
10798 /* add block delay adjustment (also possible when not blocking) */
10799 last_field_block_delay += block_delay_adjustment;
10802 #if USE_BLOCK_DELAY_BUGFIX
10803 /* when blocking enabled, correct block delay for fast movement */
10804 if (player->block_last_field &&
10805 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10806 last_field_block_delay =
10807 player->move_delay_value + player->block_delay_adjustment;
10812 #if USE_GRAVITY_BUGFIX_NEW
10813 /* when blocking enabled, correct block delay for gravity movement */
10814 if (player->block_last_field &&
10815 game.gravity && player->MovDir == MV_UP)
10816 last_field_block_delay = player->move_delay_value - 1;
10820 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10821 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10824 #if USE_NEW_MOVE_STYLE
10825 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10826 player->block_last_field) &&
10827 Feld[last_jx][last_jy] == EL_EMPTY)
10828 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10830 if (Feld[last_jx][last_jy] == EL_EMPTY)
10831 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10836 DrawPlayer(player);
10841 else if (!FrameReached(&player->actual_frame_counter, 1))
10844 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10845 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10847 #if USE_NEW_BLOCK_STYLE
10849 if (!player->block_last_field &&
10850 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10852 RemoveField(last_jx, last_jy);
10854 Feld[last_jx][last_jy] = EL_EMPTY;
10858 /* before DrawPlayer() to draw correct player graphic for this case */
10859 if (player->MovPos == 0)
10860 CheckGravityMovement(player);
10863 DrawPlayer(player); /* needed here only to cleanup last field */
10866 if (player->MovPos == 0) /* player reached destination field */
10869 if (player->move_delay_reset_counter > 0)
10871 player->move_delay_reset_counter--;
10873 if (player->move_delay_reset_counter == 0)
10875 /* continue with normal speed after quickly moving through gate */
10876 HALVE_PLAYER_SPEED(player);
10878 /* be able to make the next move without delay */
10879 player->move_delay = 0;
10883 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10885 /* continue with normal speed after quickly moving through gate */
10886 HALVE_PLAYER_SPEED(player);
10888 /* be able to make the next move without delay */
10889 player->move_delay = 0;
10893 #if USE_NEW_BLOCK_STYLE
10895 if (player->block_last_field &&
10896 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10898 RemoveField(last_jx, last_jy);
10900 Feld[last_jx][last_jy] = EL_EMPTY;
10904 player->last_jx = jx;
10905 player->last_jy = jy;
10907 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10908 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10909 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10911 DrawPlayer(player); /* needed here only to cleanup last field */
10912 RemoveHero(player);
10914 if (local_player->friends_still_needed == 0 ||
10915 IS_SP_ELEMENT(Feld[jx][jy]))
10916 player->LevelSolved = player->GameOver = TRUE;
10920 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10921 /* this breaks one level: "machine", level 000 */
10923 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10926 int move_direction = player->MovDir;
10928 int enter_side = MV_DIR_OPPOSITE(move_direction);
10929 int leave_side = move_direction;
10931 static int trigger_sides[4][2] =
10933 /* enter side leave side */
10934 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10935 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10936 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10937 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10939 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10940 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10942 int old_jx = last_jx;
10943 int old_jy = last_jy;
10944 int old_element = Feld[old_jx][old_jy];
10945 int new_element = Feld[jx][jy];
10948 /* !!! TEST ONLY !!! */
10949 if (IS_CUSTOM_ELEMENT(old_element))
10950 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10952 player->index_bit, leave_side);
10954 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10955 CE_PLAYER_LEAVES_X,
10956 player->index_bit, leave_side);
10958 if (IS_CUSTOM_ELEMENT(new_element))
10959 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10960 player->index_bit, enter_side);
10962 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10963 CE_PLAYER_ENTERS_X,
10964 player->index_bit, enter_side);
10970 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10972 TestIfHeroTouchesBadThing(jx, jy);
10973 TestIfPlayerTouchesCustomElement(jx, jy);
10976 /* needed because pushed element has not yet reached its destination,
10977 so it would trigger a change event at its previous field location */
10978 if (!player->is_pushing)
10980 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10983 if (!player->active)
10984 RemoveHero(player);
10987 if (level.use_step_counter)
10997 if (TimeLeft <= 10 && setup.time_limit)
10998 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11000 DrawGameValue_Time(TimeLeft);
11002 if (!TimeLeft && setup.time_limit)
11003 for (i = 0; i < MAX_PLAYERS; i++)
11004 KillHero(&stored_player[i]);
11006 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11007 DrawGameValue_Time(TimePlayed);
11010 if (tape.single_step && tape.recording && !tape.pausing &&
11011 !player->programmed_action)
11012 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11016 void ScrollScreen(struct PlayerInfo *player, int mode)
11018 static unsigned long screen_frame_counter = 0;
11020 if (mode == SCROLL_INIT)
11022 /* set scrolling step size according to actual player's moving speed */
11023 ScrollStepSize = TILEX / player->move_delay_value;
11025 screen_frame_counter = FrameCounter;
11026 ScreenMovDir = player->MovDir;
11027 ScreenMovPos = player->MovPos;
11028 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11031 else if (!FrameReached(&screen_frame_counter, 1))
11036 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11037 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11038 redraw_mask |= REDRAW_FIELD;
11041 ScreenMovDir = MV_NO_MOVING;
11044 void TestIfPlayerTouchesCustomElement(int x, int y)
11046 static int xy[4][2] =
11053 static int trigger_sides[4][2] =
11055 /* center side border side */
11056 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11057 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11058 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11059 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11061 static int touch_dir[4] =
11063 MV_LEFT | MV_RIGHT,
11068 int center_element = Feld[x][y]; /* should always be non-moving! */
11071 for (i = 0; i < NUM_DIRECTIONS; i++)
11073 int xx = x + xy[i][0];
11074 int yy = y + xy[i][1];
11075 int center_side = trigger_sides[i][0];
11076 int border_side = trigger_sides[i][1];
11077 int border_element;
11079 if (!IN_LEV_FIELD(xx, yy))
11082 if (IS_PLAYER(x, y))
11084 struct PlayerInfo *player = PLAYERINFO(x, y);
11086 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11087 border_element = Feld[xx][yy]; /* may be moving! */
11088 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11089 border_element = Feld[xx][yy];
11090 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11091 border_element = MovingOrBlocked2Element(xx, yy);
11093 continue; /* center and border element do not touch */
11096 /* !!! TEST ONLY !!! */
11097 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11098 player->index_bit, border_side);
11099 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11100 CE_PLAYER_TOUCHES_X,
11101 player->index_bit, border_side);
11103 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11104 CE_PLAYER_TOUCHES_X,
11105 player->index_bit, border_side);
11106 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11107 player->index_bit, border_side);
11110 else if (IS_PLAYER(xx, yy))
11112 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11114 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11116 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11117 continue; /* center and border element do not touch */
11121 /* !!! TEST ONLY !!! */
11122 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11123 player->index_bit, center_side);
11124 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11125 CE_PLAYER_TOUCHES_X,
11126 player->index_bit, center_side);
11128 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11129 CE_PLAYER_TOUCHES_X,
11130 player->index_bit, center_side);
11131 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11132 player->index_bit, center_side);
11140 void TestIfElementTouchesCustomElement(int x, int y)
11142 static int xy[4][2] =
11149 static int trigger_sides[4][2] =
11151 /* center side border side */
11152 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11153 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11154 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11155 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11157 static int touch_dir[4] =
11159 MV_LEFT | MV_RIGHT,
11164 boolean change_center_element = FALSE;
11165 int center_element_change_page = 0;
11166 int center_element = Feld[x][y]; /* should always be non-moving! */
11167 int border_trigger_element = EL_UNDEFINED;
11170 for (i = 0; i < NUM_DIRECTIONS; i++)
11172 int xx = x + xy[i][0];
11173 int yy = y + xy[i][1];
11174 int center_side = trigger_sides[i][0];
11175 int border_side = trigger_sides[i][1];
11176 int border_element;
11178 if (!IN_LEV_FIELD(xx, yy))
11181 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11182 border_element = Feld[xx][yy]; /* may be moving! */
11183 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11184 border_element = Feld[xx][yy];
11185 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11186 border_element = MovingOrBlocked2Element(xx, yy);
11188 continue; /* center and border element do not touch */
11192 /* check for change of center element (but change it only once) */
11193 if (!change_center_element)
11194 change_center_element =
11195 CheckElementChangeBySide(x, y, center_element, border_element,
11196 CE_TOUCHING_X, border_side);
11198 /* -> CheckElementChangeExtTEST */
11202 /* check for change of center element (but change it only once) */
11203 if (IS_CUSTOM_ELEMENT(center_element) &&
11204 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
11205 !change_center_element)
11207 for (j = 0; j < element_info[center_element].num_change_pages; j++)
11209 struct ElementChangeInfo *change =
11210 &element_info[center_element].change_page[j];
11212 if (change->can_change &&
11213 change->has_event[CE_TOUCHING_X] &&
11214 change->trigger_side & border_side &&
11216 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
11218 change->trigger_element == border_element
11222 change_center_element = TRUE;
11223 center_element_change_page = j;
11224 border_trigger_element = border_element;
11235 /* check for change of border element */
11236 CheckElementChangeBySide(xx, yy, border_element, center_element,
11237 CE_TOUCHING_X, center_side);
11241 /* check for change of border element */
11242 if (IS_CUSTOM_ELEMENT(border_element) &&
11243 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
11245 for (j = 0; j < element_info[border_element].num_change_pages; j++)
11247 struct ElementChangeInfo *change =
11248 &element_info[border_element].change_page[j];
11250 if (change->can_change &&
11251 change->has_event[CE_TOUCHING_X] &&
11252 change->trigger_side & center_side &&
11254 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
11256 change->trigger_element == center_element
11261 printf("::: border_element %d, %d\n", x, y);
11264 CheckElementChangeByPage(xx, yy, border_element, center_element,
11275 #if !TEST_NEW_STUFF
11277 if (change_center_element)
11280 printf("::: center_element %d, %d\n", x, y);
11283 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
11284 CE_TOUCHING_X, center_element_change_page);
11291 void TestIfElementHitsCustomElement(int x, int y, int direction)
11293 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11294 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11295 int hitx = x + dx, hity = y + dy;
11296 int hitting_element = Feld[x][y];
11297 int touched_element;
11299 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11300 !IS_FREE(hitx, hity) &&
11301 (!IS_MOVING(hitx, hity) ||
11302 MovDir[hitx][hity] != direction ||
11303 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11306 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11310 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11314 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11315 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11317 #if !USE_HITTING_SOMETHING_BUGFIX
11318 /* "hitting something" is also true when hitting the playfield border */
11319 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11320 CE_HITTING_SOMETHING, direction);
11323 if (IN_LEV_FIELD(hitx, hity))
11325 int opposite_direction = MV_DIR_OPPOSITE(direction);
11326 int hitting_side = direction;
11327 int touched_side = opposite_direction;
11329 int touched_element = MovingOrBlocked2Element(hitx, hity);
11332 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11333 MovDir[hitx][hity] != direction ||
11334 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11343 #if !USE_HIT_BY_SOMETHING_BUGFIX
11344 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11345 CE_HIT_BY_SOMETHING, opposite_direction);
11350 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11351 CE_HITTING_X, touched_side);
11355 if (IS_CUSTOM_ELEMENT(hitting_element) &&
11356 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
11358 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
11360 struct ElementChangeInfo *change =
11361 &element_info[hitting_element].change_page[i];
11363 if (change->can_change &&
11364 change->has_event[CE_HITTING_X] &&
11365 change->trigger_side & touched_side &&
11368 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
11370 change->trigger_element == touched_element
11374 CheckElementChangeByPage(x, y, hitting_element, touched_element,
11385 CheckElementChangeBySide(hitx, hity, touched_element,
11386 hitting_element, CE_HIT_BY_X, hitting_side);
11389 if (IS_CUSTOM_ELEMENT(touched_element) &&
11390 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
11392 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11394 struct ElementChangeInfo *change =
11395 &element_info[touched_element].change_page[i];
11397 if (change->can_change &&
11398 change->has_event[CE_HIT_BY_X] &&
11399 change->trigger_side & hitting_side &&
11401 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11403 change->trigger_element == hitting_element
11407 CheckElementChangeByPage(hitx, hity, touched_element,
11408 hitting_element, CE_HIT_BY_X, i);
11416 #if USE_HIT_BY_SOMETHING_BUGFIX
11417 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11418 CE_HIT_BY_SOMETHING, opposite_direction);
11423 #if USE_HITTING_SOMETHING_BUGFIX
11424 /* "hitting something" is also true when hitting the playfield border */
11425 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11426 CE_HITTING_SOMETHING, direction);
11431 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11433 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11434 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11435 int hitx = x + dx, hity = y + dy;
11436 int hitting_element = Feld[x][y];
11437 int touched_element;
11439 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11440 !IS_FREE(hitx, hity) &&
11441 (!IS_MOVING(hitx, hity) ||
11442 MovDir[hitx][hity] != direction ||
11443 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11446 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11450 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11454 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11455 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11457 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11458 EP_CAN_SMASH_EVERYTHING, direction);
11460 if (IN_LEV_FIELD(hitx, hity))
11462 int opposite_direction = MV_DIR_OPPOSITE(direction);
11463 int hitting_side = direction;
11464 int touched_side = opposite_direction;
11466 int touched_element = MovingOrBlocked2Element(hitx, hity);
11469 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11470 MovDir[hitx][hity] != direction ||
11471 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11480 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11481 CE_SMASHED_BY_SOMETHING, opposite_direction);
11485 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11486 CE_OTHER_IS_SMASHING, touched_side);
11489 if (IS_CUSTOM_ELEMENT(hitting_element) &&
11490 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
11492 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
11494 struct ElementChangeInfo *change =
11495 &element_info[hitting_element].change_page[i];
11497 if (change->can_change &&
11498 change->has_event[CE_OTHER_IS_SMASHING] &&
11499 change->trigger_side & touched_side &&
11502 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
11504 change->trigger_element == touched_element
11508 CheckElementChangeByPage(x, y, hitting_element, touched_element,
11509 CE_OTHER_IS_SMASHING, i);
11519 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11520 CE_OTHER_GETS_SMASHED, hitting_side);
11523 if (IS_CUSTOM_ELEMENT(touched_element) &&
11524 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
11526 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11528 struct ElementChangeInfo *change =
11529 &element_info[touched_element].change_page[i];
11531 if (change->can_change &&
11532 change->has_event[CE_OTHER_GETS_SMASHED] &&
11533 change->trigger_side & hitting_side &&
11535 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11537 change->trigger_element == hitting_element
11541 CheckElementChangeByPage(hitx, hity, touched_element,
11542 hitting_element, CE_OTHER_GETS_SMASHED,i);
11555 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11557 int i, kill_x = -1, kill_y = -1;
11558 int bad_element = -1;
11559 static int test_xy[4][2] =
11566 static int test_dir[4] =
11574 for (i = 0; i < NUM_DIRECTIONS; i++)
11576 int test_x, test_y, test_move_dir, test_element;
11578 test_x = good_x + test_xy[i][0];
11579 test_y = good_y + test_xy[i][1];
11581 if (!IN_LEV_FIELD(test_x, test_y))
11585 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11588 test_element = Feld[test_x][test_y];
11590 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11593 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11594 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11596 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11597 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11601 bad_element = test_element;
11607 if (kill_x != -1 || kill_y != -1)
11609 if (IS_PLAYER(good_x, good_y))
11611 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11614 if (player->shield_deadly_time_left > 0 &&
11615 !IS_INDESTRUCTIBLE(bad_element))
11616 Bang(kill_x, kill_y);
11617 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11620 if (player->shield_deadly_time_left > 0)
11621 Bang(kill_x, kill_y);
11622 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11627 Bang(good_x, good_y);
11631 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11633 int i, kill_x = -1, kill_y = -1;
11634 int bad_element = Feld[bad_x][bad_y];
11635 static int test_xy[4][2] =
11642 static int touch_dir[4] =
11644 MV_LEFT | MV_RIGHT,
11649 static int test_dir[4] =
11657 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11660 for (i = 0; i < NUM_DIRECTIONS; i++)
11662 int test_x, test_y, test_move_dir, test_element;
11664 test_x = bad_x + test_xy[i][0];
11665 test_y = bad_y + test_xy[i][1];
11666 if (!IN_LEV_FIELD(test_x, test_y))
11670 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11672 test_element = Feld[test_x][test_y];
11674 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11675 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11677 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11678 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11680 /* good thing is player or penguin that does not move away */
11681 if (IS_PLAYER(test_x, test_y))
11683 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11685 if (bad_element == EL_ROBOT && player->is_moving)
11686 continue; /* robot does not kill player if he is moving */
11688 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11690 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11691 continue; /* center and border element do not touch */
11698 else if (test_element == EL_PENGUIN)
11707 if (kill_x != -1 || kill_y != -1)
11709 if (IS_PLAYER(kill_x, kill_y))
11711 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11714 if (player->shield_deadly_time_left > 0 &&
11715 !IS_INDESTRUCTIBLE(bad_element))
11716 Bang(bad_x, bad_y);
11717 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11720 if (player->shield_deadly_time_left > 0)
11721 Bang(bad_x, bad_y);
11722 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11727 Bang(kill_x, kill_y);
11731 void TestIfHeroTouchesBadThing(int x, int y)
11733 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11736 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11738 TestIfGoodThingHitsBadThing(x, y, move_dir);
11741 void TestIfBadThingTouchesHero(int x, int y)
11743 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11746 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11748 TestIfBadThingHitsGoodThing(x, y, move_dir);
11751 void TestIfFriendTouchesBadThing(int x, int y)
11753 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11756 void TestIfBadThingTouchesFriend(int x, int y)
11758 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11761 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11763 int i, kill_x = bad_x, kill_y = bad_y;
11764 static int xy[4][2] =
11772 for (i = 0; i < NUM_DIRECTIONS; i++)
11776 x = bad_x + xy[i][0];
11777 y = bad_y + xy[i][1];
11778 if (!IN_LEV_FIELD(x, y))
11781 element = Feld[x][y];
11782 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11783 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11791 if (kill_x != bad_x || kill_y != bad_y)
11792 Bang(bad_x, bad_y);
11795 void KillHero(struct PlayerInfo *player)
11797 int jx = player->jx, jy = player->jy;
11799 if (!player->active)
11802 /* remove accessible field at the player's position */
11803 Feld[jx][jy] = EL_EMPTY;
11805 /* deactivate shield (else Bang()/Explode() would not work right) */
11806 player->shield_normal_time_left = 0;
11807 player->shield_deadly_time_left = 0;
11813 static void KillHeroUnlessEnemyProtected(int x, int y)
11815 if (!PLAYER_ENEMY_PROTECTED(x, y))
11816 KillHero(PLAYERINFO(x, y));
11819 static void KillHeroUnlessExplosionProtected(int x, int y)
11821 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11822 KillHero(PLAYERINFO(x, y));
11825 void BuryHero(struct PlayerInfo *player)
11827 int jx = player->jx, jy = player->jy;
11829 if (!player->active)
11833 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11835 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11837 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11839 player->GameOver = TRUE;
11840 RemoveHero(player);
11843 void RemoveHero(struct PlayerInfo *player)
11845 int jx = player->jx, jy = player->jy;
11846 int i, found = FALSE;
11848 player->present = FALSE;
11849 player->active = FALSE;
11851 if (!ExplodeField[jx][jy])
11852 StorePlayer[jx][jy] = 0;
11854 for (i = 0; i < MAX_PLAYERS; i++)
11855 if (stored_player[i].active)
11859 AllPlayersGone = TRUE;
11866 =============================================================================
11867 checkDiagonalPushing()
11868 -----------------------------------------------------------------------------
11869 check if diagonal input device direction results in pushing of object
11870 (by checking if the alternative direction is walkable, diggable, ...)
11871 =============================================================================
11874 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11875 int x, int y, int real_dx, int real_dy)
11877 int jx, jy, dx, dy, xx, yy;
11879 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11882 /* diagonal direction: check alternative direction */
11887 xx = jx + (dx == 0 ? real_dx : 0);
11888 yy = jy + (dy == 0 ? real_dy : 0);
11890 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11894 =============================================================================
11896 -----------------------------------------------------------------------------
11897 x, y: field next to player (non-diagonal) to try to dig to
11898 real_dx, real_dy: direction as read from input device (can be diagonal)
11899 =============================================================================
11902 int DigField(struct PlayerInfo *player,
11903 int oldx, int oldy, int x, int y,
11904 int real_dx, int real_dy, int mode)
11907 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11909 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11910 boolean player_was_pushing = player->is_pushing;
11911 int jx = oldx, jy = oldy;
11912 int dx = x - jx, dy = y - jy;
11913 int nextx = x + dx, nexty = y + dy;
11914 int move_direction = (dx == -1 ? MV_LEFT :
11915 dx == +1 ? MV_RIGHT :
11917 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11918 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11920 int dig_side = MV_DIR_OPPOSITE(move_direction);
11922 static int trigger_sides[4] =
11924 CH_SIDE_RIGHT, /* moving left */
11925 CH_SIDE_LEFT, /* moving right */
11926 CH_SIDE_BOTTOM, /* moving up */
11927 CH_SIDE_TOP, /* moving down */
11929 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11931 int old_element = Feld[jx][jy];
11934 if (is_player) /* function can also be called by EL_PENGUIN */
11936 if (player->MovPos == 0)
11938 player->is_digging = FALSE;
11939 player->is_collecting = FALSE;
11942 if (player->MovPos == 0) /* last pushing move finished */
11943 player->is_pushing = FALSE;
11945 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11947 player->is_switching = FALSE;
11948 #if USE_NEW_PUSH_DELAY
11949 player->push_delay = -1;
11951 player->push_delay = 0;
11954 return MF_NO_ACTION;
11958 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11959 return MF_NO_ACTION;
11964 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11966 if (IS_TUBE(Feld[jx][jy]) ||
11967 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11971 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11972 int tube_leave_directions[][2] =
11974 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11975 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11976 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11977 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11978 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11979 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11980 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11981 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11982 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11983 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11984 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11985 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11988 while (tube_leave_directions[i][0] != tube_element)
11991 if (tube_leave_directions[i][0] == -1) /* should not happen */
11995 if (!(tube_leave_directions[i][1] & move_direction))
11996 return MF_NO_ACTION; /* tube has no opening in this direction */
12001 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12002 old_element = Back[jx][jy];
12003 #if USE_BACK_WALKABLE_BUGFIX
12005 /* in case of element dropped at player position, check background */
12006 else if (Back[jx][jy] != EL_EMPTY &&
12007 game.engine_version >= VERSION_IDENT(2,2,0,0))
12008 old_element = Back[jx][jy];
12013 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12014 return MF_NO_ACTION; /* field has no opening in this direction */
12016 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12017 return MF_NO_ACTION; /* field has no opening in this direction */
12019 element = Feld[x][y];
12021 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12022 return MF_NO_ACTION;
12024 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12025 game.engine_version >= VERSION_IDENT(2,2,0,0))
12026 return MF_NO_ACTION;
12029 if (game.gravity && is_player && !player->is_auto_moving &&
12030 canFallDown(player) && move_direction != MV_DOWN &&
12031 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12032 return MF_NO_ACTION; /* player cannot walk here due to gravity */
12036 if (element == EL_EMPTY_SPACE &&
12037 game.gravity && !player->is_auto_moving &&
12038 canFallDown(player) && move_direction != MV_DOWN)
12039 return MF_NO_ACTION; /* player cannot walk here due to gravity */
12045 case EL_SP_PORT_LEFT:
12046 case EL_SP_PORT_RIGHT:
12047 case EL_SP_PORT_UP:
12048 case EL_SP_PORT_DOWN:
12049 case EL_SP_PORT_HORIZONTAL:
12050 case EL_SP_PORT_VERTICAL:
12051 case EL_SP_PORT_ANY:
12052 case EL_SP_GRAVITY_PORT_LEFT:
12053 case EL_SP_GRAVITY_PORT_RIGHT:
12054 case EL_SP_GRAVITY_PORT_UP:
12055 case EL_SP_GRAVITY_PORT_DOWN:
12057 if (!canEnterSupaplexPort(x, y, dx, dy))
12058 return MF_NO_ACTION;
12061 element != EL_SP_PORT_LEFT &&
12062 element != EL_SP_GRAVITY_PORT_LEFT &&
12063 element != EL_SP_PORT_HORIZONTAL &&
12064 element != EL_SP_PORT_ANY) ||
12066 element != EL_SP_PORT_RIGHT &&
12067 element != EL_SP_GRAVITY_PORT_RIGHT &&
12068 element != EL_SP_PORT_HORIZONTAL &&
12069 element != EL_SP_PORT_ANY) ||
12071 element != EL_SP_PORT_UP &&
12072 element != EL_SP_GRAVITY_PORT_UP &&
12073 element != EL_SP_PORT_VERTICAL &&
12074 element != EL_SP_PORT_ANY) ||
12076 element != EL_SP_PORT_DOWN &&
12077 element != EL_SP_GRAVITY_PORT_DOWN &&
12078 element != EL_SP_PORT_VERTICAL &&
12079 element != EL_SP_PORT_ANY) ||
12080 !IN_LEV_FIELD(nextx, nexty) ||
12081 !IS_FREE(nextx, nexty))
12082 return MF_NO_ACTION;
12085 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12086 element == EL_SP_GRAVITY_PORT_RIGHT ||
12087 element == EL_SP_GRAVITY_PORT_UP ||
12088 element == EL_SP_GRAVITY_PORT_DOWN)
12089 game.gravity = !game.gravity;
12091 /* automatically move to the next field with double speed */
12092 player->programmed_action = move_direction;
12094 if (player->move_delay_reset_counter == 0)
12096 player->move_delay_reset_counter = 2; /* two double speed steps */
12098 DOUBLE_PLAYER_SPEED(player);
12101 player->move_delay_reset_counter = 2;
12103 DOUBLE_PLAYER_SPEED(player);
12107 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
12110 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
12116 case EL_TUBE_VERTICAL:
12117 case EL_TUBE_HORIZONTAL:
12118 case EL_TUBE_VERTICAL_LEFT:
12119 case EL_TUBE_VERTICAL_RIGHT:
12120 case EL_TUBE_HORIZONTAL_UP:
12121 case EL_TUBE_HORIZONTAL_DOWN:
12122 case EL_TUBE_LEFT_UP:
12123 case EL_TUBE_LEFT_DOWN:
12124 case EL_TUBE_RIGHT_UP:
12125 case EL_TUBE_RIGHT_DOWN:
12128 int tube_enter_directions[][2] =
12130 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
12131 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
12132 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
12133 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
12134 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
12135 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
12136 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
12137 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
12138 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
12139 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
12140 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
12141 { -1, MV_NO_MOVING }
12144 while (tube_enter_directions[i][0] != element)
12147 if (tube_enter_directions[i][0] == -1) /* should not happen */
12151 if (!(tube_enter_directions[i][1] & move_direction))
12152 return MF_NO_ACTION; /* tube has no opening in this direction */
12154 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
12162 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12164 if (IS_WALKABLE(element))
12167 int sound_element = SND_ELEMENT(element);
12168 int sound_action = ACTION_WALKING;
12171 if (!ACCESS_FROM(element, opposite_direction))
12172 return MF_NO_ACTION; /* field not accessible from this direction */
12176 if (element == EL_EMPTY_SPACE &&
12177 game.gravity && !player->is_auto_moving &&
12178 canFallDown(player) && move_direction != MV_DOWN)
12179 return MF_NO_ACTION; /* player cannot walk here due to gravity */
12182 if (IS_RND_GATE(element))
12184 if (!player->key[RND_GATE_NR(element)])
12185 return MF_NO_ACTION;
12187 else if (IS_RND_GATE_GRAY(element))
12189 if (!player->key[RND_GATE_GRAY_NR(element)])
12190 return MF_NO_ACTION;
12192 else if (element == EL_EXIT_OPEN ||
12193 element == EL_SP_EXIT_OPEN ||
12194 element == EL_SP_EXIT_OPENING)
12196 sound_action = ACTION_PASSING; /* player is passing exit */
12198 else if (element == EL_EMPTY)
12200 sound_action = ACTION_MOVING; /* nothing to walk on */
12203 /* play sound from background or player, whatever is available */
12204 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12205 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12207 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
12212 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
12214 else if (IS_PASSABLE(element))
12218 if (!canPassField(x, y, move_direction))
12219 return MF_NO_ACTION;
12224 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
12225 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
12226 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
12227 return MF_NO_ACTION;
12229 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
12230 return MF_NO_ACTION;
12235 if (!ACCESS_FROM(element, opposite_direction))
12236 return MF_NO_ACTION; /* field not accessible from this direction */
12238 if (IS_CUSTOM_ELEMENT(element) &&
12239 !ACCESS_FROM(element, opposite_direction))
12240 return MF_NO_ACTION; /* field not accessible from this direction */
12244 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12245 return MF_NO_ACTION;
12250 if (IS_EM_GATE(element))
12252 if (!player->key[EM_GATE_NR(element)])
12253 return MF_NO_ACTION;
12255 else if (IS_EM_GATE_GRAY(element))
12257 if (!player->key[EM_GATE_GRAY_NR(element)])
12258 return MF_NO_ACTION;
12260 else if (IS_SP_PORT(element))
12262 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12263 element == EL_SP_GRAVITY_PORT_RIGHT ||
12264 element == EL_SP_GRAVITY_PORT_UP ||
12265 element == EL_SP_GRAVITY_PORT_DOWN)
12266 game.gravity = !game.gravity;
12267 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12268 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12269 element == EL_SP_GRAVITY_ON_PORT_UP ||
12270 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12271 game.gravity = TRUE;
12272 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12273 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12274 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12275 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12276 game.gravity = FALSE;
12279 /* automatically move to the next field with double speed */
12280 player->programmed_action = move_direction;
12282 if (player->move_delay_reset_counter == 0)
12284 player->move_delay_reset_counter = 2; /* two double speed steps */
12286 DOUBLE_PLAYER_SPEED(player);
12289 player->move_delay_reset_counter = 2;
12291 DOUBLE_PLAYER_SPEED(player);
12294 PlayLevelSoundAction(x, y, ACTION_PASSING);
12298 else if (IS_DIGGABLE(element))
12302 if (mode != DF_SNAP)
12305 GfxElement[x][y] = GFX_ELEMENT(element);
12308 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
12310 player->is_digging = TRUE;
12313 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12315 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12316 player->index_bit, dig_side);
12319 if (mode == DF_SNAP)
12320 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12325 else if (IS_COLLECTIBLE(element))
12329 if (is_player && mode != DF_SNAP)
12331 GfxElement[x][y] = element;
12332 player->is_collecting = TRUE;
12335 if (element == EL_SPEED_PILL)
12337 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12339 else if (element == EL_EXTRA_TIME && level.time > 0)
12342 DrawGameValue_Time(TimeLeft);
12344 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12346 player->shield_normal_time_left += 10;
12347 if (element == EL_SHIELD_DEADLY)
12348 player->shield_deadly_time_left += 10;
12350 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
12352 if (player->inventory_size < MAX_INVENTORY_SIZE)
12353 player->inventory_element[player->inventory_size++] = element;
12355 DrawGameValue_Dynamite(local_player->inventory_size);
12357 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12359 player->dynabomb_count++;
12360 player->dynabombs_left++;
12362 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12364 player->dynabomb_size++;
12366 else if (element == EL_DYNABOMB_INCREASE_POWER)
12368 player->dynabomb_xl = TRUE;
12370 else if (IS_KEY(element))
12372 player->key[KEY_NR(element)] = TRUE;
12374 DrawGameValue_Keys(player->key);
12376 redraw_mask |= REDRAW_DOOR_1;
12378 else if (IS_ENVELOPE(element))
12381 player->show_envelope = element;
12383 ShowEnvelope(element - EL_ENVELOPE_1);
12386 else if (IS_DROPPABLE(element) ||
12387 IS_THROWABLE(element)) /* can be collected and dropped */
12391 if (element_info[element].collect_count == 0)
12392 player->inventory_infinite_element = element;
12394 for (i = 0; i < element_info[element].collect_count; i++)
12395 if (player->inventory_size < MAX_INVENTORY_SIZE)
12396 player->inventory_element[player->inventory_size++] = element;
12398 DrawGameValue_Dynamite(local_player->inventory_size);
12400 else if (element_info[element].collect_count > 0)
12402 local_player->gems_still_needed -=
12403 element_info[element].collect_count;
12404 if (local_player->gems_still_needed < 0)
12405 local_player->gems_still_needed = 0;
12407 DrawGameValue_Emeralds(local_player->gems_still_needed);
12410 RaiseScoreElement(element);
12411 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12414 CheckTriggeredElementChangeByPlayer(x, y, element,
12415 CE_PLAYER_COLLECTS_X,
12416 player->index_bit, dig_side);
12419 if (mode == DF_SNAP)
12420 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12425 else if (IS_PUSHABLE(element))
12427 if (mode == DF_SNAP && element != EL_BD_ROCK)
12428 return MF_NO_ACTION;
12430 if (CAN_FALL(element) && dy)
12431 return MF_NO_ACTION;
12433 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12434 !(element == EL_SPRING && level.use_spring_bug))
12435 return MF_NO_ACTION;
12438 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12439 ((move_direction & MV_VERTICAL &&
12440 ((element_info[element].move_pattern & MV_LEFT &&
12441 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12442 (element_info[element].move_pattern & MV_RIGHT &&
12443 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12444 (move_direction & MV_HORIZONTAL &&
12445 ((element_info[element].move_pattern & MV_UP &&
12446 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12447 (element_info[element].move_pattern & MV_DOWN &&
12448 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12449 return MF_NO_ACTION;
12453 /* do not push elements already moving away faster than player */
12454 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12455 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12456 return MF_NO_ACTION;
12458 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
12459 return MF_NO_ACTION;
12465 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12467 if (player->push_delay_value == -1 || !player_was_pushing)
12468 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12470 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12472 if (player->push_delay_value == -1)
12473 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12476 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12478 if (player->push_delay_value == -1 || !player_was_pushing)
12479 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12482 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12484 if (!player->is_pushing)
12485 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12489 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
12490 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
12491 !player_is_pushing))
12492 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12495 if (!player->is_pushing &&
12496 game.engine_version >= VERSION_IDENT(2,2,0,7))
12497 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12501 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
12502 player->push_delay, player->push_delay_value,
12503 FrameCounter, game.engine_version,
12504 player_was_pushing, player->is_pushing,
12505 element, element_info[element].token_name,
12506 GET_NEW_PUSH_DELAY(element));
12509 player->is_pushing = TRUE;
12511 if (!(IN_LEV_FIELD(nextx, nexty) &&
12512 (IS_FREE(nextx, nexty) ||
12513 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12514 IS_SB_ELEMENT(element)))))
12515 return MF_NO_ACTION;
12517 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12518 return MF_NO_ACTION;
12520 #if USE_NEW_PUSH_DELAY
12523 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
12524 printf("::: ALERT: %d, %d [%d / %d]\n",
12525 player->push_delay, player->push_delay2,
12526 FrameCounter, FrameCounter / 50);
12529 if (player->push_delay == -1) /* new pushing; restart delay */
12530 player->push_delay = 0;
12532 if (player->push_delay == 0) /* new pushing; restart delay */
12533 player->push_delay = FrameCounter;
12536 #if USE_NEW_PUSH_DELAY
12538 if ( (player->push_delay > 0) != (!xxx_fr) )
12539 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
12540 player->push_delay,
12541 xxx_pdv2, player->push_delay2, player->push_delay_value,
12542 FrameCounter, FrameCounter / 50);
12546 if (player->push_delay > 0 &&
12547 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12548 element != EL_SPRING && element != EL_BALLOON)
12551 if (player->push_delay < player->push_delay_value &&
12552 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12553 element != EL_SPRING && element != EL_BALLOON)
12557 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12558 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12559 element != EL_SPRING && element != EL_BALLOON)
12562 /* make sure that there is no move delay before next try to push */
12563 #if USE_NEW_MOVE_DELAY
12564 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12565 player->move_delay = 0;
12567 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12568 player->move_delay = INITIAL_MOVE_DELAY_OFF;
12571 return MF_NO_ACTION;
12575 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12578 if (IS_SB_ELEMENT(element))
12580 if (element == EL_SOKOBAN_FIELD_FULL)
12582 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12583 local_player->sokobanfields_still_needed++;
12586 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12588 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12589 local_player->sokobanfields_still_needed--;
12592 Feld[x][y] = EL_SOKOBAN_OBJECT;
12594 if (Back[x][y] == Back[nextx][nexty])
12595 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12596 else if (Back[x][y] != 0)
12597 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12600 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12603 if (local_player->sokobanfields_still_needed == 0 &&
12604 game.emulation == EMU_SOKOBAN)
12606 player->LevelSolved = player->GameOver = TRUE;
12607 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12611 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12613 InitMovingField(x, y, move_direction);
12614 GfxAction[x][y] = ACTION_PUSHING;
12616 if (mode == DF_SNAP)
12617 ContinueMoving(x, y);
12619 MovPos[x][y] = (dx != 0 ? dx : dy);
12621 Pushed[x][y] = TRUE;
12622 Pushed[nextx][nexty] = TRUE;
12624 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12625 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12627 player->push_delay_value = -1; /* get new value later */
12629 #if USE_PUSH_BUGFIX
12630 /* now: check for element change _after_ element has been pushed! */
12632 if (game.use_change_when_pushing_bug)
12634 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12637 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12638 player->index_bit, dig_side);
12639 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12640 player->index_bit, dig_side);
12646 /* check for element change _after_ element has been pushed! */
12650 /* !!! TEST ONLY !!! */
12651 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12652 player->index_bit, dig_side);
12653 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12654 player->index_bit, dig_side);
12656 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12657 player->index_bit, dig_side);
12658 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12659 player->index_bit, dig_side);
12667 else if (IS_SWITCHABLE(element))
12669 if (PLAYER_SWITCHING(player, x, y))
12671 CheckTriggeredElementChangeByPlayer(x,y, element,
12672 CE_PLAYER_PRESSES_X,
12673 player->index_bit, dig_side);
12678 player->is_switching = TRUE;
12679 player->switch_x = x;
12680 player->switch_y = y;
12682 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12684 if (element == EL_ROBOT_WHEEL)
12686 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12690 DrawLevelField(x, y);
12692 else if (element == EL_SP_TERMINAL)
12696 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12698 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12700 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12701 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12704 else if (IS_BELT_SWITCH(element))
12706 ToggleBeltSwitch(x, y);
12708 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12709 element == EL_SWITCHGATE_SWITCH_DOWN)
12711 ToggleSwitchgateSwitch(x, y);
12713 else if (element == EL_LIGHT_SWITCH ||
12714 element == EL_LIGHT_SWITCH_ACTIVE)
12716 ToggleLightSwitch(x, y);
12719 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12720 SND_LIGHT_SWITCH_ACTIVATING :
12721 SND_LIGHT_SWITCH_DEACTIVATING);
12724 else if (element == EL_TIMEGATE_SWITCH)
12726 ActivateTimegateSwitch(x, y);
12728 else if (element == EL_BALLOON_SWITCH_LEFT ||
12729 element == EL_BALLOON_SWITCH_RIGHT ||
12730 element == EL_BALLOON_SWITCH_UP ||
12731 element == EL_BALLOON_SWITCH_DOWN ||
12732 element == EL_BALLOON_SWITCH_ANY)
12734 if (element == EL_BALLOON_SWITCH_ANY)
12735 game.balloon_dir = move_direction;
12737 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12738 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12739 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12740 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12743 else if (element == EL_LAMP)
12745 Feld[x][y] = EL_LAMP_ACTIVE;
12746 local_player->lights_still_needed--;
12748 ResetGfxAnimation(x, y);
12749 DrawLevelField(x, y);
12751 else if (element == EL_TIME_ORB_FULL)
12753 Feld[x][y] = EL_TIME_ORB_EMPTY;
12755 DrawGameValue_Time(TimeLeft);
12757 ResetGfxAnimation(x, y);
12758 DrawLevelField(x, y);
12761 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12765 CheckTriggeredElementChangeByPlayer(x, y, element,
12767 player->index_bit, dig_side);
12769 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12770 player->index_bit, dig_side);
12776 if (!PLAYER_SWITCHING(player, x, y))
12778 player->is_switching = TRUE;
12779 player->switch_x = x;
12780 player->switch_y = y;
12783 /* !!! TEST ONLY !!! */
12784 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12785 player->index_bit, dig_side);
12786 CheckTriggeredElementChangeByPlayer(x, y, element,
12788 player->index_bit, dig_side);
12790 CheckTriggeredElementChangeByPlayer(x, y, element,
12792 player->index_bit, dig_side);
12793 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12794 player->index_bit, dig_side);
12799 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12800 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12801 player->index_bit, dig_side);
12802 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12803 player->index_bit, dig_side);
12805 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12806 player->index_bit, dig_side);
12807 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12808 player->index_bit, dig_side);
12812 return MF_NO_ACTION;
12815 #if USE_NEW_PUSH_DELAY
12816 player->push_delay = -1;
12818 player->push_delay = 0;
12821 #if USE_PENGUIN_COLLECT_BUGFIX
12822 if (is_player) /* function can also be called by EL_PENGUIN */
12825 if (Feld[x][y] != element) /* really digged/collected something */
12826 player->is_collecting = !player->is_digging;
12832 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12834 int jx = player->jx, jy = player->jy;
12835 int x = jx + dx, y = jy + dy;
12836 int snap_direction = (dx == -1 ? MV_LEFT :
12837 dx == +1 ? MV_RIGHT :
12839 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12842 if (player->MovPos != 0)
12845 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12849 if (!player->active || !IN_LEV_FIELD(x, y))
12857 if (player->MovPos == 0)
12858 player->is_pushing = FALSE;
12860 player->is_snapping = FALSE;
12862 if (player->MovPos == 0)
12864 player->is_moving = FALSE;
12865 player->is_digging = FALSE;
12866 player->is_collecting = FALSE;
12872 if (player->is_snapping)
12875 player->MovDir = snap_direction;
12878 if (player->MovPos == 0)
12881 player->is_moving = FALSE;
12882 player->is_digging = FALSE;
12883 player->is_collecting = FALSE;
12886 player->is_dropping = FALSE;
12888 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12891 player->is_snapping = TRUE;
12894 if (player->MovPos == 0)
12897 player->is_moving = FALSE;
12898 player->is_digging = FALSE;
12899 player->is_collecting = FALSE;
12903 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12904 DrawLevelField(player->last_jx, player->last_jy);
12907 DrawLevelField(x, y);
12916 boolean DropElement(struct PlayerInfo *player)
12918 int old_element, new_element;
12919 int dropx = player->jx, dropy = player->jy;
12920 int drop_direction = player->MovDir;
12922 int drop_side = drop_direction;
12924 static int trigger_sides[4] =
12926 CH_SIDE_LEFT, /* dropping left */
12927 CH_SIDE_RIGHT, /* dropping right */
12928 CH_SIDE_TOP, /* dropping up */
12929 CH_SIDE_BOTTOM, /* dropping down */
12931 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12933 int drop_element = (player->inventory_size > 0 ?
12934 player->inventory_element[player->inventory_size - 1] :
12935 player->inventory_infinite_element != EL_UNDEFINED ?
12936 player->inventory_infinite_element :
12937 player->dynabombs_left > 0 ?
12938 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12941 #if USE_DROP_BUGFIX
12942 /* do not drop an element on top of another element; when holding drop key
12943 pressed without moving, dropped element must move away before the next
12944 element can be dropped (this is especially important if the next element
12945 is dynamite, which can be placed on background for historical reasons) */
12946 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12950 if (IS_THROWABLE(drop_element))
12952 dropx += GET_DX_FROM_DIR(drop_direction);
12953 dropy += GET_DY_FROM_DIR(drop_direction);
12955 if (!IN_LEV_FIELD(dropx, dropy))
12959 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12960 new_element = drop_element; /* default: no change when dropping */
12962 /* check if player is active, not moving and ready to drop */
12963 if (!player->active || player->MovPos || player->drop_delay > 0)
12966 /* check if player has anything that can be dropped */
12968 if (new_element == EL_UNDEFINED)
12971 if (player->inventory_size == 0 &&
12972 player->inventory_infinite_element == EL_UNDEFINED &&
12973 player->dynabombs_left == 0)
12977 /* check if anything can be dropped at the current position */
12978 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12981 /* collected custom elements can only be dropped on empty fields */
12983 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12986 if (player->inventory_size > 0 &&
12987 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12988 && old_element != EL_EMPTY)
12992 if (old_element != EL_EMPTY)
12993 Back[dropx][dropy] = old_element; /* store old element on this field */
12995 ResetGfxAnimation(dropx, dropy);
12996 ResetRandomAnimationValue(dropx, dropy);
12998 if (player->inventory_size > 0 ||
12999 player->inventory_infinite_element != EL_UNDEFINED)
13001 if (player->inventory_size > 0)
13003 player->inventory_size--;
13006 new_element = player->inventory_element[player->inventory_size];
13009 DrawGameValue_Dynamite(local_player->inventory_size);
13011 if (new_element == EL_DYNAMITE)
13012 new_element = EL_DYNAMITE_ACTIVE;
13013 else if (new_element == EL_SP_DISK_RED)
13014 new_element = EL_SP_DISK_RED_ACTIVE;
13017 Feld[dropx][dropy] = new_element;
13019 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13020 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13021 el2img(Feld[dropx][dropy]), 0);
13023 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13026 /* needed if previous element just changed to "empty" in the last frame */
13027 Changed[dropx][dropy] = FALSE; /* allow another change */
13031 /* !!! TEST ONLY !!! */
13032 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13033 player->index_bit, drop_side);
13034 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13036 player->index_bit, drop_side);
13038 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13040 player->index_bit, drop_side);
13041 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13042 player->index_bit, drop_side);
13045 TestIfElementTouchesCustomElement(dropx, dropy);
13047 else /* player is dropping a dyna bomb */
13049 player->dynabombs_left--;
13052 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
13055 Feld[dropx][dropy] = new_element;
13057 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13058 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13059 el2img(Feld[dropx][dropy]), 0);
13061 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13068 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13071 InitField_WithBug1(dropx, dropy, FALSE);
13073 InitField(dropx, dropy, FALSE);
13074 if (CAN_MOVE(Feld[dropx][dropy]))
13075 InitMovDir(dropx, dropy);
13079 new_element = Feld[dropx][dropy]; /* element might have changed */
13081 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13082 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13085 int move_stepsize = element_info[new_element].move_stepsize;
13087 int move_direction, nextx, nexty;
13089 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13090 MovDir[dropx][dropy] = drop_direction;
13092 move_direction = MovDir[dropx][dropy];
13093 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13094 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13097 Changed[dropx][dropy] = FALSE; /* allow another change */
13098 CheckCollision[dropx][dropy] = 2;
13101 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
13104 WasJustMoving[dropx][dropy] = 3;
13107 InitMovingField(dropx, dropy, move_direction);
13108 ContinueMoving(dropx, dropy);
13113 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
13116 Changed[dropx][dropy] = FALSE; /* allow another change */
13119 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
13121 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
13122 CE_HITTING_SOMETHING, move_direction);
13130 player->drop_delay = 2 * TILEX / move_stepsize + 1;
13135 player->drop_delay = 8 + 8 + 8;
13139 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13144 player->is_dropping = TRUE;
13146 #if USE_DROP_BUGFIX
13147 player->drop_x = dropx;
13148 player->drop_y = dropy;
13154 /* ------------------------------------------------------------------------- */
13155 /* game sound playing functions */
13156 /* ------------------------------------------------------------------------- */
13158 static int *loop_sound_frame = NULL;
13159 static int *loop_sound_volume = NULL;
13161 void InitPlayLevelSound()
13163 int num_sounds = getSoundListSize();
13165 checked_free(loop_sound_frame);
13166 checked_free(loop_sound_volume);
13168 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13169 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13172 static void PlayLevelSound(int x, int y, int nr)
13174 int sx = SCREENX(x), sy = SCREENY(y);
13175 int volume, stereo_position;
13176 int max_distance = 8;
13177 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13179 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13180 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13183 if (!IN_LEV_FIELD(x, y) ||
13184 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13185 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13188 volume = SOUND_MAX_VOLUME;
13190 if (!IN_SCR_FIELD(sx, sy))
13192 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13193 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13195 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13198 stereo_position = (SOUND_MAX_LEFT +
13199 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13200 (SCR_FIELDX + 2 * max_distance));
13202 if (IS_LOOP_SOUND(nr))
13204 /* This assures that quieter loop sounds do not overwrite louder ones,
13205 while restarting sound volume comparison with each new game frame. */
13207 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13210 loop_sound_volume[nr] = volume;
13211 loop_sound_frame[nr] = FrameCounter;
13214 PlaySoundExt(nr, volume, stereo_position, type);
13217 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13219 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13220 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13221 y < LEVELY(BY1) ? LEVELY(BY1) :
13222 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13226 static void PlayLevelSoundAction(int x, int y, int action)
13228 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13231 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13233 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13235 if (sound_effect != SND_UNDEFINED)
13236 PlayLevelSound(x, y, sound_effect);
13239 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13242 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13244 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13245 PlayLevelSound(x, y, sound_effect);
13248 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13250 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13252 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13253 PlayLevelSound(x, y, sound_effect);
13256 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13258 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13260 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13261 StopSound(sound_effect);
13264 static void PlayLevelMusic()
13266 if (levelset.music[level_nr] != MUS_UNDEFINED)
13267 PlayMusic(levelset.music[level_nr]); /* from config file */
13269 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13272 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
13274 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13277 if (sample == SAMPLE_bug)
13278 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
13284 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13288 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13292 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13296 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13300 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13304 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13308 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13311 case SAMPLE_android_clone:
13312 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13315 case SAMPLE_android_move:
13316 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13319 case SAMPLE_spring:
13320 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13324 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
13328 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13331 case SAMPLE_eater_eat:
13332 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13336 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13339 case SAMPLE_collect:
13340 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13343 case SAMPLE_diamond:
13344 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13347 case SAMPLE_squash:
13348 /* !!! CHECK THIS !!! */
13350 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13352 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13356 case SAMPLE_wonderfall:
13357 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13361 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13365 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13369 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13373 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13377 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13381 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13384 case SAMPLE_wonder:
13385 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13389 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13392 case SAMPLE_exit_open:
13393 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13396 case SAMPLE_exit_leave:
13397 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13400 case SAMPLE_dynamite:
13401 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13405 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13409 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13413 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13417 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13421 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13425 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13429 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13434 void RaiseScore(int value)
13436 local_player->score += value;
13438 DrawGameValue_Score(local_player->score);
13441 void RaiseScoreElement(int element)
13446 case EL_BD_DIAMOND:
13447 case EL_EMERALD_YELLOW:
13448 case EL_EMERALD_RED:
13449 case EL_EMERALD_PURPLE:
13450 case EL_SP_INFOTRON:
13451 RaiseScore(level.score[SC_EMERALD]);
13454 RaiseScore(level.score[SC_DIAMOND]);
13457 RaiseScore(level.score[SC_CRYSTAL]);
13460 RaiseScore(level.score[SC_PEARL]);
13463 case EL_BD_BUTTERFLY:
13464 case EL_SP_ELECTRON:
13465 RaiseScore(level.score[SC_BUG]);
13468 case EL_BD_FIREFLY:
13469 case EL_SP_SNIKSNAK:
13470 RaiseScore(level.score[SC_SPACESHIP]);
13473 case EL_DARK_YAMYAM:
13474 RaiseScore(level.score[SC_YAMYAM]);
13477 RaiseScore(level.score[SC_ROBOT]);
13480 RaiseScore(level.score[SC_PACMAN]);
13483 RaiseScore(level.score[SC_NUT]);
13486 case EL_SP_DISK_RED:
13487 case EL_DYNABOMB_INCREASE_NUMBER:
13488 case EL_DYNABOMB_INCREASE_SIZE:
13489 case EL_DYNABOMB_INCREASE_POWER:
13490 RaiseScore(level.score[SC_DYNAMITE]);
13492 case EL_SHIELD_NORMAL:
13493 case EL_SHIELD_DEADLY:
13494 RaiseScore(level.score[SC_SHIELD]);
13496 case EL_EXTRA_TIME:
13497 RaiseScore(level.score[SC_TIME_BONUS]);
13511 RaiseScore(level.score[SC_KEY]);
13514 RaiseScore(element_info[element].collect_score);
13519 void RequestQuitGame(boolean ask_if_really_quit)
13521 if (AllPlayersGone ||
13522 !ask_if_really_quit ||
13523 level_editor_test_game ||
13524 Request("Do you really want to quit the game ?",
13525 REQ_ASK | REQ_STAY_CLOSED))
13527 #if defined(NETWORK_AVALIABLE)
13528 if (options.network)
13529 SendToServer_StopPlaying();
13533 game_status = GAME_MODE_MAIN;
13541 if (tape.playing && tape.deactivate_display)
13542 TapeDeactivateDisplayOff(TRUE);
13545 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13548 if (tape.playing && tape.deactivate_display)
13549 TapeDeactivateDisplayOn();
13556 /* ---------- new game button stuff ---------------------------------------- */
13558 /* graphic position values for game buttons */
13559 #define GAME_BUTTON_XSIZE 30
13560 #define GAME_BUTTON_YSIZE 30
13561 #define GAME_BUTTON_XPOS 5
13562 #define GAME_BUTTON_YPOS 215
13563 #define SOUND_BUTTON_XPOS 5
13564 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13566 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13567 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13568 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13569 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13570 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13571 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13578 } gamebutton_info[NUM_GAME_BUTTONS] =
13581 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13586 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13587 GAME_CTRL_ID_PAUSE,
13591 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13596 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13597 SOUND_CTRL_ID_MUSIC,
13598 "background music on/off"
13601 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13602 SOUND_CTRL_ID_LOOPS,
13603 "sound loops on/off"
13606 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13607 SOUND_CTRL_ID_SIMPLE,
13608 "normal sounds on/off"
13612 void CreateGameButtons()
13616 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13618 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13619 struct GadgetInfo *gi;
13622 unsigned long event_mask;
13623 int gd_xoffset, gd_yoffset;
13624 int gd_x1, gd_x2, gd_y1, gd_y2;
13627 gd_xoffset = gamebutton_info[i].x;
13628 gd_yoffset = gamebutton_info[i].y;
13629 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13630 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13632 if (id == GAME_CTRL_ID_STOP ||
13633 id == GAME_CTRL_ID_PAUSE ||
13634 id == GAME_CTRL_ID_PLAY)
13636 button_type = GD_TYPE_NORMAL_BUTTON;
13638 event_mask = GD_EVENT_RELEASED;
13639 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13640 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13644 button_type = GD_TYPE_CHECK_BUTTON;
13646 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13647 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13648 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13649 event_mask = GD_EVENT_PRESSED;
13650 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13651 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13654 gi = CreateGadget(GDI_CUSTOM_ID, id,
13655 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13656 GDI_X, DX + gd_xoffset,
13657 GDI_Y, DY + gd_yoffset,
13658 GDI_WIDTH, GAME_BUTTON_XSIZE,
13659 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13660 GDI_TYPE, button_type,
13661 GDI_STATE, GD_BUTTON_UNPRESSED,
13662 GDI_CHECKED, checked,
13663 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13664 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13665 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13666 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13667 GDI_EVENT_MASK, event_mask,
13668 GDI_CALLBACK_ACTION, HandleGameButtons,
13672 Error(ERR_EXIT, "cannot create gadget");
13674 game_gadget[id] = gi;
13678 void FreeGameButtons()
13682 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13683 FreeGadget(game_gadget[i]);
13686 static void MapGameButtons()
13690 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13691 MapGadget(game_gadget[i]);
13694 void UnmapGameButtons()
13698 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13699 UnmapGadget(game_gadget[i]);
13702 static void HandleGameButtons(struct GadgetInfo *gi)
13704 int id = gi->custom_id;
13706 if (game_status != GAME_MODE_PLAYING)
13711 case GAME_CTRL_ID_STOP:
13712 RequestQuitGame(TRUE);
13715 case GAME_CTRL_ID_PAUSE:
13716 if (options.network)
13718 #if defined(NETWORK_AVALIABLE)
13720 SendToServer_ContinuePlaying();
13722 SendToServer_PausePlaying();
13726 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13729 case GAME_CTRL_ID_PLAY:
13732 #if defined(NETWORK_AVALIABLE)
13733 if (options.network)
13734 SendToServer_ContinuePlaying();
13738 tape.pausing = FALSE;
13739 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13744 case SOUND_CTRL_ID_MUSIC:
13745 if (setup.sound_music)
13747 setup.sound_music = FALSE;
13750 else if (audio.music_available)
13752 setup.sound = setup.sound_music = TRUE;
13754 SetAudioMode(setup.sound);
13760 case SOUND_CTRL_ID_LOOPS:
13761 if (setup.sound_loops)
13762 setup.sound_loops = FALSE;
13763 else if (audio.loops_available)
13765 setup.sound = setup.sound_loops = TRUE;
13766 SetAudioMode(setup.sound);
13770 case SOUND_CTRL_ID_SIMPLE:
13771 if (setup.sound_simple)
13772 setup.sound_simple = FALSE;
13773 else if (audio.sound_available)
13775 setup.sound = setup.sound_simple = TRUE;
13776 SetAudioMode(setup.sound);