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)
69 /* for MovePlayer() */
70 #define MF_NO_ACTION 0
74 /* for ScrollPlayer() */
76 #define SCROLL_GO_ON 1
79 #define EX_PHASE_START 0
80 #define EX_TYPE_NONE 0
81 #define EX_TYPE_NORMAL (1 << 0)
82 #define EX_TYPE_CENTER (1 << 1)
83 #define EX_TYPE_BORDER (1 << 2)
84 #define EX_TYPE_CROSS (1 << 3)
85 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
87 /* special positions in the game control window (relative to control window) */
90 #define XX_EMERALDS 29
91 #define YY_EMERALDS 54
92 #define XX_DYNAMITE 29
93 #define YY_DYNAMITE 89
102 /* special positions in the game control window (relative to main window) */
103 #define DX_LEVEL (DX + XX_LEVEL)
104 #define DY_LEVEL (DY + YY_LEVEL)
105 #define DX_EMERALDS (DX + XX_EMERALDS)
106 #define DY_EMERALDS (DY + YY_EMERALDS)
107 #define DX_DYNAMITE (DX + XX_DYNAMITE)
108 #define DY_DYNAMITE (DY + YY_DYNAMITE)
109 #define DX_KEYS (DX + XX_KEYS)
110 #define DY_KEYS (DY + YY_KEYS)
111 #define DX_SCORE (DX + XX_SCORE)
112 #define DY_SCORE (DY + YY_SCORE)
113 #define DX_TIME1 (DX + XX_TIME1)
114 #define DX_TIME2 (DX + XX_TIME2)
115 #define DY_TIME (DY + YY_TIME)
117 /* values for initial player move delay (initial delay counter value) */
118 #define INITIAL_MOVE_DELAY_OFF -1
119 #define INITIAL_MOVE_DELAY_ON 0
121 /* values for player movement speed (which is in fact a delay value) */
122 #define MOVE_DELAY_NORMAL_SPEED 8
123 #define MOVE_DELAY_HIGH_SPEED 4
125 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
126 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
127 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
128 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
130 /* values for other actions */
131 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
133 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
134 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
136 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
138 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
139 RND(element_info[e].push_delay_random))
140 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
141 RND(element_info[e].drop_delay_random))
142 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
143 RND(element_info[e].move_delay_random))
144 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
145 (element_info[e].move_delay_random))
147 #define GET_TARGET_ELEMENT(e, ch) \
148 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
149 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
151 #define GET_VALID_PLAYER_ELEMENT(e) \
152 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
154 #define CAN_GROW_INTO(e) \
155 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
157 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
158 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
161 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 (CAN_MOVE_INTO_ACID(e) && \
164 Feld[x][y] == EL_ACID) || \
167 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
168 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
169 (CAN_MOVE_INTO_ACID(e) && \
170 Feld[x][y] == EL_ACID) || \
173 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
174 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
176 (CAN_MOVE_INTO_ACID(e) && \
177 Feld[x][y] == EL_ACID) || \
178 (DONT_COLLIDE_WITH(e) && \
180 !PLAYER_ENEMY_PROTECTED(x, y))))
183 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
184 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
186 (DONT_COLLIDE_WITH(e) && \
188 !PLAYER_ENEMY_PROTECTED(x, y))))
191 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
192 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
195 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
198 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
203 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
206 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
211 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
214 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
217 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
220 #define PIG_CAN_ENTER_FIELD(e, x, y) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
223 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
224 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
225 IS_FOOD_PENGUIN(Feld[x][y])))
226 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
227 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
229 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
230 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
232 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
233 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
237 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
238 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
239 (CAN_MOVE_INTO_ACID(e) && \
240 Feld[x][y] == EL_ACID) || \
241 Feld[x][y] == EL_DIAMOND))
243 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
244 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
245 (CAN_MOVE_INTO_ACID(e) && \
246 Feld[x][y] == EL_ACID) || \
247 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
249 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
250 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
251 (CAN_MOVE_INTO_ACID(e) && \
252 Feld[x][y] == EL_ACID) || \
253 IS_AMOEBOID(Feld[x][y])))
255 #define PIG_CAN_ENTER_FIELD(e, x, y) \
256 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
257 (CAN_MOVE_INTO_ACID(e) && \
258 Feld[x][y] == EL_ACID) || \
259 IS_FOOD_PIG(Feld[x][y])))
261 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
262 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
263 (CAN_MOVE_INTO_ACID(e) && \
264 Feld[x][y] == EL_ACID) || \
265 IS_FOOD_PENGUIN(Feld[x][y]) || \
266 Feld[x][y] == EL_EXIT_OPEN))
268 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
269 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
270 (CAN_MOVE_INTO_ACID(e) && \
271 Feld[x][y] == EL_ACID)))
273 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
274 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
275 (CAN_MOVE_INTO_ACID(e) && \
276 Feld[x][y] == EL_ACID) || \
279 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
280 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
281 (CAN_MOVE_INTO_ACID(e) && \
282 Feld[x][y] == EL_ACID)))
286 #define GROUP_NR(e) ((e) - EL_GROUP_START)
287 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
288 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
289 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
291 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
292 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
295 #define CE_ENTER_FIELD_COND(e, x, y) \
296 (!IS_PLAYER(x, y) && \
297 (Feld[x][y] == EL_ACID || \
298 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
300 #define CE_ENTER_FIELD_COND(e, x, y) \
301 (!IS_PLAYER(x, y) && \
302 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
305 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
306 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
308 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
309 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
311 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
312 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
313 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
314 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
316 /* game button identifiers */
317 #define GAME_CTRL_ID_STOP 0
318 #define GAME_CTRL_ID_PAUSE 1
319 #define GAME_CTRL_ID_PLAY 2
320 #define SOUND_CTRL_ID_MUSIC 3
321 #define SOUND_CTRL_ID_LOOPS 4
322 #define SOUND_CTRL_ID_SIMPLE 5
324 #define NUM_GAME_BUTTONS 6
327 /* forward declaration for internal use */
329 static void AdvanceFrameAndPlayerCounters(int);
331 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
332 static boolean MovePlayer(struct PlayerInfo *, int, int);
333 static void ScrollPlayer(struct PlayerInfo *, int);
334 static void ScrollScreen(struct PlayerInfo *, int);
336 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
338 static void InitBeltMovement(void);
339 static void CloseAllOpenTimegates(void);
340 static void CheckGravityMovement(struct PlayerInfo *);
341 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
342 static void KillHeroUnlessEnemyProtected(int, int);
343 static void KillHeroUnlessExplosionProtected(int, int);
345 static void TestIfPlayerTouchesCustomElement(int, int);
346 static void TestIfElementTouchesCustomElement(int, int);
347 static void TestIfElementHitsCustomElement(int, int, int);
349 static void TestIfElementSmashesCustomElement(int, int, int);
352 static void ChangeElement(int, int, int);
354 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
355 #define CheckTriggeredElementChange(x, y, e, ev) \
356 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
358 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
359 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
360 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
361 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
362 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
363 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
366 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
367 #define CheckElementChange(x, y, e, te, ev) \
368 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
369 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
370 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
371 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
372 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
373 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
374 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
376 static void PlayLevelSound(int, int, int);
377 static void PlayLevelSoundNearest(int, int, int);
378 static void PlayLevelSoundAction(int, int, int);
379 static void PlayLevelSoundElementAction(int, int, int, int);
380 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
381 static void PlayLevelSoundActionIfLoop(int, int, int);
382 static void StopLevelSoundActionIfLoop(int, int, int);
383 static void PlayLevelMusic();
385 static void MapGameButtons();
386 static void HandleGameButtons(struct GadgetInfo *);
388 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
391 /* ------------------------------------------------------------------------- */
392 /* definition of elements that automatically change to other elements after */
393 /* a specified time, eventually calling a function when changing */
394 /* ------------------------------------------------------------------------- */
396 /* forward declaration for changer functions */
397 static void InitBuggyBase(int x, int y);
398 static void WarnBuggyBase(int x, int y);
400 static void InitTrap(int x, int y);
401 static void ActivateTrap(int x, int y);
402 static void ChangeActiveTrap(int x, int y);
404 static void InitRobotWheel(int x, int y);
405 static void RunRobotWheel(int x, int y);
406 static void StopRobotWheel(int x, int y);
408 static void InitTimegateWheel(int x, int y);
409 static void RunTimegateWheel(int x, int y);
411 struct ChangingElementInfo
416 void (*pre_change_function)(int x, int y);
417 void (*change_function)(int x, int y);
418 void (*post_change_function)(int x, int y);
421 static struct ChangingElementInfo change_delay_list[] =
472 EL_SWITCHGATE_OPENING,
480 EL_SWITCHGATE_CLOSING,
481 EL_SWITCHGATE_CLOSED,
513 EL_ACID_SPLASH_RIGHT,
522 EL_SP_BUGGY_BASE_ACTIVATING,
529 EL_SP_BUGGY_BASE_ACTIVATING,
530 EL_SP_BUGGY_BASE_ACTIVE,
537 EL_SP_BUGGY_BASE_ACTIVE,
561 EL_ROBOT_WHEEL_ACTIVE,
569 EL_TIMEGATE_SWITCH_ACTIVE,
590 int push_delay_fixed, push_delay_random;
595 { EL_BALLOON, 0, 0 },
597 { EL_SOKOBAN_OBJECT, 2, 0 },
598 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
599 { EL_SATELLITE, 2, 0 },
600 { EL_SP_DISK_YELLOW, 2, 0 },
602 { EL_UNDEFINED, 0, 0 },
610 move_stepsize_list[] =
612 { EL_AMOEBA_DROP, 2 },
613 { EL_AMOEBA_DROPPING, 2 },
614 { EL_QUICKSAND_FILLING, 1 },
615 { EL_QUICKSAND_EMPTYING, 1 },
616 { EL_MAGIC_WALL_FILLING, 2 },
617 { EL_BD_MAGIC_WALL_FILLING, 2 },
618 { EL_MAGIC_WALL_EMPTYING, 2 },
619 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
629 collect_count_list[] =
632 { EL_BD_DIAMOND, 1 },
633 { EL_EMERALD_YELLOW, 1 },
634 { EL_EMERALD_RED, 1 },
635 { EL_EMERALD_PURPLE, 1 },
637 { EL_SP_INFOTRON, 1 },
649 access_direction_list[] =
651 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
652 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
653 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
654 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
655 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
656 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
657 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
658 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
659 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
660 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
661 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
663 { EL_SP_PORT_LEFT, MV_RIGHT },
664 { EL_SP_PORT_RIGHT, MV_LEFT },
665 { EL_SP_PORT_UP, MV_DOWN },
666 { EL_SP_PORT_DOWN, MV_UP },
667 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
668 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
669 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
670 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
671 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
672 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
673 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
674 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
675 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
676 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
677 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
678 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
679 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
680 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
681 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
683 { EL_UNDEFINED, MV_NO_MOVING }
686 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
688 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
689 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
690 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
691 IS_JUST_CHANGING(x, y))
693 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
696 void GetPlayerConfig()
698 if (!audio.sound_available)
699 setup.sound_simple = FALSE;
701 if (!audio.loops_available)
702 setup.sound_loops = FALSE;
704 if (!audio.music_available)
705 setup.sound_music = FALSE;
707 if (!video.fullscreen_available)
708 setup.fullscreen = FALSE;
710 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
712 SetAudioMode(setup.sound);
716 static int getBeltNrFromBeltElement(int element)
718 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
719 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
720 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
723 static int getBeltNrFromBeltActiveElement(int element)
725 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
726 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
727 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
730 static int getBeltNrFromBeltSwitchElement(int element)
732 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
733 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
734 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
737 static int getBeltDirNrFromBeltSwitchElement(int element)
739 static int belt_base_element[4] =
741 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
742 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
743 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
744 EL_CONVEYOR_BELT_4_SWITCH_LEFT
747 int belt_nr = getBeltNrFromBeltSwitchElement(element);
748 int belt_dir_nr = element - belt_base_element[belt_nr];
750 return (belt_dir_nr % 3);
753 static int getBeltDirFromBeltSwitchElement(int element)
755 static int belt_move_dir[3] =
762 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
764 return belt_move_dir[belt_dir_nr];
767 static void InitPlayerField(int x, int y, int element, boolean init_game)
769 if (element == EL_SP_MURPHY)
773 if (stored_player[0].present)
775 Feld[x][y] = EL_SP_MURPHY_CLONE;
781 stored_player[0].use_murphy_graphic = TRUE;
784 Feld[x][y] = EL_PLAYER_1;
790 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
791 int jx = player->jx, jy = player->jy;
793 player->present = TRUE;
795 player->block_last_field = (element == EL_SP_MURPHY ?
796 level.sp_block_last_field :
797 level.block_last_field);
799 #if USE_NEW_BLOCK_STYLE
802 /* ---------- initialize player's last field block delay --------------- */
804 /* always start with reliable default value (no adjustment needed) */
805 player->block_delay_adjustment = 0;
807 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
808 if (player->block_last_field && element == EL_SP_MURPHY)
809 player->block_delay_adjustment = 1;
811 /* special case 2: in game engines before 3.1.1, blocking was different */
812 if (game.use_block_last_field_bug)
813 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
816 /* blocking the last field when moving was corrected in version 3.1.1 */
817 if (game.use_block_last_field_bug)
819 /* even "not blocking" was blocking the last field for one frame */
820 level.block_delay = (level.block_last_field ? 7 : 1);
821 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
823 level.block_last_field = TRUE;
824 level.sp_block_last_field = TRUE;
828 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
829 level.block_delay = 8; /* when blocking, block 8 frames */
830 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
834 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
840 player->block_delay = (player->block_last_field ?
841 (element == EL_SP_MURPHY ?
842 level.sp_block_delay :
843 level.block_delay) : 0);
845 player->block_delay = (element == EL_SP_MURPHY ?
846 (player->block_last_field ? 7 : 1) :
847 (player->block_last_field ? 7 : 1));
853 printf("::: block_last_field == %d, block_delay = %d\n",
854 player->block_last_field, player->block_delay);
858 if (!options.network || player->connected)
860 player->active = TRUE;
862 /* remove potentially duplicate players */
863 if (StorePlayer[jx][jy] == Feld[x][y])
864 StorePlayer[jx][jy] = 0;
866 StorePlayer[x][y] = Feld[x][y];
870 printf("Player %d activated.\n", player->element_nr);
871 printf("[Local player is %d and currently %s.]\n",
872 local_player->element_nr,
873 local_player->active ? "active" : "not active");
877 Feld[x][y] = EL_EMPTY;
879 player->jx = player->last_jx = x;
880 player->jy = player->last_jy = y;
884 static void InitField(int x, int y, boolean init_game)
886 int element = Feld[x][y];
895 InitPlayerField(x, y, element, init_game);
898 case EL_SOKOBAN_FIELD_PLAYER:
899 element = Feld[x][y] = EL_PLAYER_1;
900 InitField(x, y, init_game);
902 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
903 InitField(x, y, init_game);
906 case EL_SOKOBAN_FIELD_EMPTY:
907 local_player->sokobanfields_still_needed++;
911 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
912 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
913 else if (x > 0 && Feld[x-1][y] == EL_ACID)
914 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
915 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
916 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
917 else if (y > 0 && Feld[x][y-1] == EL_ACID)
918 Feld[x][y] = EL_ACID_POOL_BOTTOM;
919 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
920 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
928 case EL_SPACESHIP_RIGHT:
929 case EL_SPACESHIP_UP:
930 case EL_SPACESHIP_LEFT:
931 case EL_SPACESHIP_DOWN:
933 case EL_BD_BUTTERFLY_RIGHT:
934 case EL_BD_BUTTERFLY_UP:
935 case EL_BD_BUTTERFLY_LEFT:
936 case EL_BD_BUTTERFLY_DOWN:
937 case EL_BD_BUTTERFLY:
938 case EL_BD_FIREFLY_RIGHT:
939 case EL_BD_FIREFLY_UP:
940 case EL_BD_FIREFLY_LEFT:
941 case EL_BD_FIREFLY_DOWN:
943 case EL_PACMAN_RIGHT:
967 if (y == lev_fieldy - 1)
969 Feld[x][y] = EL_AMOEBA_GROWING;
970 Store[x][y] = EL_AMOEBA_WET;
974 case EL_DYNAMITE_ACTIVE:
975 case EL_SP_DISK_RED_ACTIVE:
976 case EL_DYNABOMB_PLAYER_1_ACTIVE:
977 case EL_DYNABOMB_PLAYER_2_ACTIVE:
978 case EL_DYNABOMB_PLAYER_3_ACTIVE:
979 case EL_DYNABOMB_PLAYER_4_ACTIVE:
984 local_player->lights_still_needed++;
988 local_player->friends_still_needed++;
993 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
998 Feld[x][y] = EL_EMPTY;
1003 case EL_EM_KEY_1_FILE:
1004 Feld[x][y] = EL_EM_KEY_1;
1006 case EL_EM_KEY_2_FILE:
1007 Feld[x][y] = EL_EM_KEY_2;
1009 case EL_EM_KEY_3_FILE:
1010 Feld[x][y] = EL_EM_KEY_3;
1012 case EL_EM_KEY_4_FILE:
1013 Feld[x][y] = EL_EM_KEY_4;
1017 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1018 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1019 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1020 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1021 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1022 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1023 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1024 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1025 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1026 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1027 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1028 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1031 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1032 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1033 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1035 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1037 game.belt_dir[belt_nr] = belt_dir;
1038 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1040 else /* more than one switch -- set it like the first switch */
1042 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1047 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1049 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1052 case EL_LIGHT_SWITCH_ACTIVE:
1054 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1058 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1060 else if (IS_GROUP_ELEMENT(element))
1062 struct ElementGroupInfo *group = element_info[element].group;
1063 int last_anim_random_frame = gfx.anim_random_frame;
1066 if (group->choice_mode == ANIM_RANDOM)
1067 gfx.anim_random_frame = RND(group->num_elements_resolved);
1069 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1070 group->choice_mode, 0,
1073 if (group->choice_mode == ANIM_RANDOM)
1074 gfx.anim_random_frame = last_anim_random_frame;
1076 group->choice_pos++;
1078 Feld[x][y] = group->element_resolved[element_pos];
1080 InitField(x, y, init_game);
1086 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1088 InitField(x, y, init_game);
1090 /* not needed to call InitMovDir() -- already done by InitField()! */
1091 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1092 CAN_MOVE(Feld[x][y]))
1096 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1098 int old_element = Feld[x][y];
1100 InitField(x, y, init_game);
1102 /* not needed to call InitMovDir() -- already done by InitField()! */
1103 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1104 CAN_MOVE(old_element) &&
1105 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1108 /* this case is in fact a combination of not less than three bugs:
1109 first, it calls InitMovDir() for elements that can move, although this is
1110 already done by InitField(); then, it checks the element that was at this
1111 field _before_ the call to InitField() (which can change it); lastly, it
1112 was not called for "mole with direction" elements, which were treated as
1113 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1117 inline void DrawGameValue_Emeralds(int value)
1119 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1122 inline void DrawGameValue_Dynamite(int value)
1124 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1127 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1131 /* currently only 4 of 8 possible keys are displayed */
1132 for (i = 0; i < STD_NUM_KEYS; i++)
1134 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1135 el2edimg(EL_KEY_1 + i));
1138 inline void DrawGameValue_Score(int value)
1140 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1143 inline void DrawGameValue_Time(int value)
1146 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1148 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1151 inline void DrawGameValue_Level(int value)
1154 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1157 /* misuse area for displaying emeralds to draw bigger level number */
1158 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1159 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1161 /* now copy it to the area for displaying level number */
1162 BlitBitmap(drawto, drawto,
1163 DX_EMERALDS, DY_EMERALDS + 1,
1164 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1165 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1166 DX_LEVEL - 1, DY_LEVEL + 1);
1168 /* restore the area for displaying emeralds */
1169 DrawGameValue_Emeralds(local_player->gems_still_needed);
1171 /* yes, this is all really ugly :-) */
1175 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1178 int key[MAX_NUM_KEYS];
1181 for (i = 0; i < MAX_NUM_KEYS; i++)
1182 key[i] = key_bits & (1 << i);
1184 DrawGameValue_Level(level_nr);
1186 DrawGameValue_Emeralds(emeralds);
1187 DrawGameValue_Dynamite(dynamite);
1188 DrawGameValue_Score(score);
1189 DrawGameValue_Time(time);
1191 DrawGameValue_Keys(key);
1194 void DrawGameDoorValues()
1198 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1200 DrawGameDoorValues_EM();
1205 DrawGameValue_Level(level_nr);
1207 DrawGameValue_Emeralds(local_player->gems_still_needed);
1208 DrawGameValue_Dynamite(local_player->inventory_size);
1209 DrawGameValue_Score(local_player->score);
1210 DrawGameValue_Time(TimeLeft);
1212 for (i = 0; i < MAX_PLAYERS; i++)
1213 DrawGameValue_Keys(stored_player[i].key);
1216 static void resolve_group_element(int group_element, int recursion_depth)
1218 static int group_nr;
1219 static struct ElementGroupInfo *group;
1220 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1223 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1225 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1226 group_element - EL_GROUP_START + 1);
1228 /* replace element which caused too deep recursion by question mark */
1229 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1234 if (recursion_depth == 0) /* initialization */
1236 group = element_info[group_element].group;
1237 group_nr = group_element - EL_GROUP_START;
1239 group->num_elements_resolved = 0;
1240 group->choice_pos = 0;
1243 for (i = 0; i < actual_group->num_elements; i++)
1245 int element = actual_group->element[i];
1247 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1250 if (IS_GROUP_ELEMENT(element))
1251 resolve_group_element(element, recursion_depth + 1);
1254 group->element_resolved[group->num_elements_resolved++] = element;
1255 element_info[element].in_group[group_nr] = TRUE;
1260 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1262 printf("::: group %d: %d resolved elements\n",
1263 group_element - EL_GROUP_START, group->num_elements_resolved);
1264 for (i = 0; i < group->num_elements_resolved; i++)
1265 printf("::: - %d ['%s']\n", group->element_resolved[i],
1266 element_info[group->element_resolved[i]].token_name);
1273 =============================================================================
1275 -----------------------------------------------------------------------------
1276 initialize game engine due to level / tape version number
1277 =============================================================================
1280 static void InitGameEngine()
1284 /* set game engine from tape file when re-playing, else from level file */
1285 game.engine_version = (tape.playing ? tape.engine_version :
1286 level.game_version);
1288 /* ---------------------------------------------------------------------- */
1289 /* set flags for bugs and changes according to active game engine version */
1290 /* ---------------------------------------------------------------------- */
1293 Summary of bugfix/change:
1294 Fixed handling for custom elements that change when pushed by the player.
1296 Fixed/changed in version:
1300 Before 3.1.0, custom elements that "change when pushing" changed directly
1301 after the player started pushing them (until then handled in "DigField()").
1302 Since 3.1.0, these custom elements are not changed until the "pushing"
1303 move of the element is finished (now handled in "ContinueMoving()").
1305 Affected levels/tapes:
1306 The first condition is generally needed for all levels/tapes before version
1307 3.1.0, which might use the old behaviour before it was changed; known tapes
1308 that are affected are some tapes from the level set "Walpurgis Gardens" by
1310 The second condition is an exception from the above case and is needed for
1311 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1312 above (including some development versions of 3.1.0), but before it was
1313 known that this change would break tapes like the above and was fixed in
1314 3.1.1, so that the changed behaviour was active although the engine version
1315 while recording maybe was before 3.1.0. There is at least one tape that is
1316 affected by this exception, which is the tape for the one-level set "Bug
1317 Machine" by Juergen Bonhagen.
1320 game.use_change_when_pushing_bug =
1321 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1323 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1324 tape.game_version < VERSION_IDENT(3,1,1,0)));
1327 Summary of bugfix/change:
1328 Fixed handling for blocking the field the player leaves when moving.
1330 Fixed/changed in version:
1334 Before 3.1.1, when "block last field when moving" was enabled, the field
1335 the player is leaving when moving was blocked for the time of the move,
1336 and was directly unblocked afterwards. This resulted in the last field
1337 being blocked for exactly one less than the number of frames of one player
1338 move. Additionally, even when blocking was disabled, the last field was
1339 blocked for exactly one frame.
1340 Since 3.1.1, due to changes in player movement handling, the last field
1341 is not blocked at all when blocking is disabled. When blocking is enabled,
1342 the last field is blocked for exactly the number of frames of one player
1343 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1344 last field is blocked for exactly one more than the number of frames of
1347 Affected levels/tapes:
1348 (!!! yet to be determined -- probably many !!!)
1351 game.use_block_last_field_bug =
1352 (game.engine_version < VERSION_IDENT(3,1,1,0));
1354 /* ---------------------------------------------------------------------- */
1356 /* dynamically adjust element properties according to game engine version */
1357 InitElementPropertiesEngine(game.engine_version);
1360 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1361 printf(" tape version == %06d [%s] [file: %06d]\n",
1362 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1364 printf(" => game.engine_version == %06d\n", game.engine_version);
1367 /* ---------- recursively resolve group elements ------------------------- */
1369 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1370 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1371 element_info[i].in_group[j] = FALSE;
1373 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1374 resolve_group_element(EL_GROUP_START + i, 0);
1376 /* ---------- initialize player's initial move delay --------------------- */
1378 #if USE_NEW_MOVE_DELAY
1379 /* dynamically adjust player properties according to level information */
1380 game.initial_move_delay_value =
1381 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1383 /* dynamically adjust player properties according to game engine version */
1384 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1385 game.initial_move_delay_value : 0);
1387 /* dynamically adjust player properties according to game engine version */
1388 game.initial_move_delay =
1389 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1390 INITIAL_MOVE_DELAY_OFF);
1392 /* dynamically adjust player properties according to level information */
1393 game.initial_move_delay_value =
1394 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1397 /* ---------- initialize player's initial push delay --------------------- */
1399 /* dynamically adjust player properties according to game engine version */
1400 game.initial_push_delay_value =
1401 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1403 /* ---------- initialize changing elements ------------------------------- */
1405 /* initialize changing elements information */
1406 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1408 struct ElementInfo *ei = &element_info[i];
1410 /* this pointer might have been changed in the level editor */
1411 ei->change = &ei->change_page[0];
1413 if (!IS_CUSTOM_ELEMENT(i))
1415 ei->change->target_element = EL_EMPTY_SPACE;
1416 ei->change->delay_fixed = 0;
1417 ei->change->delay_random = 0;
1418 ei->change->delay_frames = 1;
1421 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1423 ei->has_change_event[j] = FALSE;
1425 ei->event_page_nr[j] = 0;
1426 ei->event_page[j] = &ei->change_page[0];
1430 /* add changing elements from pre-defined list */
1431 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1433 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1434 struct ElementInfo *ei = &element_info[ch_delay->element];
1436 ei->change->target_element = ch_delay->target_element;
1437 ei->change->delay_fixed = ch_delay->change_delay;
1439 ei->change->pre_change_function = ch_delay->pre_change_function;
1440 ei->change->change_function = ch_delay->change_function;
1441 ei->change->post_change_function = ch_delay->post_change_function;
1443 ei->has_change_event[CE_DELAY] = TRUE;
1446 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1451 /* add change events from custom element configuration */
1452 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1454 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1456 for (j = 0; j < ei->num_change_pages; j++)
1458 if (!ei->change_page[j].can_change)
1461 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1463 /* only add event page for the first page found with this event */
1464 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1466 ei->has_change_event[k] = TRUE;
1468 ei->event_page_nr[k] = j;
1469 ei->event_page[k] = &ei->change_page[j];
1477 /* add change events from custom element configuration */
1478 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1480 int element = EL_CUSTOM_START + i;
1482 /* only add custom elements that change after fixed/random frame delay */
1483 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1484 element_info[element].has_change_event[CE_DELAY] = TRUE;
1488 /* ---------- initialize run-time trigger player and element ------------- */
1490 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1492 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1494 for (j = 0; j < ei->num_change_pages; j++)
1496 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1497 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1501 /* ---------- initialize trigger events ---------------------------------- */
1503 /* initialize trigger events information */
1504 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1505 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1506 trigger_events[i][j] = FALSE;
1509 /* add trigger events from element change event properties */
1510 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1512 struct ElementInfo *ei = &element_info[i];
1514 for (j = 0; j < ei->num_change_pages; j++)
1516 if (!ei->change_page[j].can_change)
1519 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1521 int trigger_element = ei->change_page[j].trigger_element;
1523 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1525 if (ei->change_page[j].has_event[k])
1527 if (IS_GROUP_ELEMENT(trigger_element))
1529 struct ElementGroupInfo *group =
1530 element_info[trigger_element].group;
1532 for (l = 0; l < group->num_elements_resolved; l++)
1533 trigger_events[group->element_resolved[l]][k] = TRUE;
1536 trigger_events[trigger_element][k] = TRUE;
1543 /* add trigger events from element change event properties */
1544 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1545 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1546 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1547 if (element_info[i].change->has_event[j])
1548 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1551 /* ---------- initialize push delay -------------------------------------- */
1553 /* initialize push delay values to default */
1554 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1556 if (!IS_CUSTOM_ELEMENT(i))
1558 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1559 element_info[i].push_delay_random = game.default_push_delay_random;
1563 /* set push delay value for certain elements from pre-defined list */
1564 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1566 int e = push_delay_list[i].element;
1568 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1569 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1572 /* set push delay value for Supaplex elements for newer engine versions */
1573 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1575 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1577 if (IS_SP_ELEMENT(i))
1579 #if USE_NEW_MOVE_STYLE
1580 /* set SP push delay to just enough to push under a falling zonk */
1581 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1583 element_info[i].push_delay_fixed = delay;
1584 element_info[i].push_delay_random = 0;
1586 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1587 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1593 /* ---------- initialize move stepsize ----------------------------------- */
1595 /* initialize move stepsize values to default */
1596 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1597 if (!IS_CUSTOM_ELEMENT(i))
1598 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1600 /* set move stepsize value for certain elements from pre-defined list */
1601 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1603 int e = move_stepsize_list[i].element;
1605 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1609 /* ---------- initialize move dig/leave ---------------------------------- */
1611 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1613 element_info[i].can_leave_element = FALSE;
1614 element_info[i].can_leave_element_last = FALSE;
1618 /* ---------- initialize gem count --------------------------------------- */
1620 /* initialize gem count values for each element */
1621 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1622 if (!IS_CUSTOM_ELEMENT(i))
1623 element_info[i].collect_count = 0;
1625 /* add gem count values for all elements from pre-defined list */
1626 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1627 element_info[collect_count_list[i].element].collect_count =
1628 collect_count_list[i].count;
1630 /* ---------- initialize access direction -------------------------------- */
1632 /* initialize access direction values to default (access from every side) */
1633 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1634 if (!IS_CUSTOM_ELEMENT(i))
1635 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1637 /* set access direction value for certain elements from pre-defined list */
1638 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1639 element_info[access_direction_list[i].element].access_direction =
1640 access_direction_list[i].direction;
1645 =============================================================================
1647 -----------------------------------------------------------------------------
1648 initialize and start new game
1649 =============================================================================
1654 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1655 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1656 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1663 #if USE_NEW_AMOEBA_CODE
1664 printf("Using new amoeba code.\n");
1666 printf("Using old amoeba code.\n");
1671 /* don't play tapes over network */
1672 network_playing = (options.network && !tape.playing);
1674 for (i = 0; i < MAX_PLAYERS; i++)
1676 struct PlayerInfo *player = &stored_player[i];
1678 player->index_nr = i;
1679 player->index_bit = (1 << i);
1680 player->element_nr = EL_PLAYER_1 + i;
1682 player->present = FALSE;
1683 player->active = FALSE;
1686 player->effective_action = 0;
1687 player->programmed_action = 0;
1690 player->gems_still_needed = level.gems_needed;
1691 player->sokobanfields_still_needed = 0;
1692 player->lights_still_needed = 0;
1693 player->friends_still_needed = 0;
1695 for (j = 0; j < MAX_NUM_KEYS; j++)
1696 player->key[j] = FALSE;
1698 player->dynabomb_count = 0;
1699 player->dynabomb_size = 1;
1700 player->dynabombs_left = 0;
1701 player->dynabomb_xl = FALSE;
1703 player->MovDir = MV_NO_MOVING;
1706 player->GfxDir = MV_NO_MOVING;
1707 player->GfxAction = ACTION_DEFAULT;
1709 player->StepFrame = 0;
1711 player->use_murphy_graphic = FALSE;
1713 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1714 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1716 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1718 player->actual_frame_counter = 0;
1720 player->step_counter = 0;
1722 player->last_move_dir = MV_NO_MOVING;
1724 player->is_waiting = FALSE;
1725 player->is_moving = FALSE;
1726 player->is_auto_moving = FALSE;
1727 player->is_digging = FALSE;
1728 player->is_snapping = FALSE;
1729 player->is_collecting = FALSE;
1730 player->is_pushing = FALSE;
1731 player->is_switching = FALSE;
1732 player->is_dropping = FALSE;
1734 player->is_bored = FALSE;
1735 player->is_sleeping = FALSE;
1737 player->frame_counter_bored = -1;
1738 player->frame_counter_sleeping = -1;
1740 player->anim_delay_counter = 0;
1741 player->post_delay_counter = 0;
1743 player->action_waiting = ACTION_DEFAULT;
1744 player->last_action_waiting = ACTION_DEFAULT;
1745 player->special_action_bored = ACTION_DEFAULT;
1746 player->special_action_sleeping = ACTION_DEFAULT;
1748 player->num_special_action_bored = 0;
1749 player->num_special_action_sleeping = 0;
1751 /* determine number of special actions for bored and sleeping animation */
1752 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1754 boolean found = FALSE;
1756 for (k = 0; k < NUM_DIRECTIONS; k++)
1757 if (el_act_dir2img(player->element_nr, j, k) !=
1758 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1762 player->num_special_action_bored++;
1766 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1768 boolean found = FALSE;
1770 for (k = 0; k < NUM_DIRECTIONS; k++)
1771 if (el_act_dir2img(player->element_nr, j, k) !=
1772 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1776 player->num_special_action_sleeping++;
1781 player->switch_x = -1;
1782 player->switch_y = -1;
1785 player->drop_x = -1;
1786 player->drop_y = -1;
1789 player->show_envelope = 0;
1791 player->move_delay = game.initial_move_delay;
1792 player->move_delay_value = game.initial_move_delay_value;
1794 player->move_delay_reset_counter = 0;
1796 #if USE_NEW_PUSH_DELAY
1797 player->push_delay = -1; /* initialized when pushing starts */
1798 player->push_delay_value = game.initial_push_delay_value;
1800 player->push_delay = 0;
1801 player->push_delay_value = game.initial_push_delay_value;
1804 player->drop_delay = 0;
1806 player->last_jx = player->last_jy = 0;
1807 player->jx = player->jy = 0;
1809 player->shield_normal_time_left = 0;
1810 player->shield_deadly_time_left = 0;
1812 player->inventory_infinite_element = EL_UNDEFINED;
1813 player->inventory_size = 0;
1815 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1816 SnapField(player, 0, 0);
1818 player->LevelSolved = FALSE;
1819 player->GameOver = FALSE;
1822 network_player_action_received = FALSE;
1824 #if defined(NETWORK_AVALIABLE)
1825 /* initial null action */
1826 if (network_playing)
1827 SendToServer_MovePlayer(MV_NO_MOVING);
1836 TimeLeft = level.time;
1839 ScreenMovDir = MV_NO_MOVING;
1843 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1845 AllPlayersGone = FALSE;
1847 game.yamyam_content_nr = 0;
1848 game.magic_wall_active = FALSE;
1849 game.magic_wall_time_left = 0;
1850 game.light_time_left = 0;
1851 game.timegate_time_left = 0;
1852 game.switchgate_pos = 0;
1853 game.balloon_dir = MV_NO_MOVING;
1854 game.gravity = level.initial_gravity;
1855 game.explosions_delayed = TRUE;
1857 game.envelope_active = FALSE;
1859 for (i = 0; i < NUM_BELTS; i++)
1861 game.belt_dir[i] = MV_NO_MOVING;
1862 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1865 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1866 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1868 for (x = 0; x < lev_fieldx; x++)
1870 for (y = 0; y < lev_fieldy; y++)
1872 Feld[x][y] = level.field[x][y];
1873 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1874 ChangeDelay[x][y] = 0;
1875 ChangePage[x][y] = -1;
1876 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1878 WasJustMoving[x][y] = 0;
1879 WasJustFalling[x][y] = 0;
1880 CheckCollision[x][y] = 0;
1882 Pushed[x][y] = FALSE;
1884 Changed[x][y] = FALSE;
1885 ChangeEvent[x][y] = -1;
1887 ExplodePhase[x][y] = 0;
1888 ExplodeDelay[x][y] = 0;
1889 ExplodeField[x][y] = EX_TYPE_NONE;
1891 RunnerVisit[x][y] = 0;
1892 PlayerVisit[x][y] = 0;
1895 GfxRandom[x][y] = INIT_GFX_RANDOM();
1896 GfxElement[x][y] = EL_UNDEFINED;
1897 GfxAction[x][y] = ACTION_DEFAULT;
1898 GfxDir[x][y] = MV_NO_MOVING;
1902 for (y = 0; y < lev_fieldy; y++)
1904 for (x = 0; x < lev_fieldx; x++)
1906 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1908 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1910 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1913 InitField(x, y, TRUE);
1919 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1920 emulate_sb ? EMU_SOKOBAN :
1921 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1923 /* initialize explosion and ignition delay */
1924 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1926 if (!IS_CUSTOM_ELEMENT(i))
1929 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1930 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1931 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1932 int last_phase = (num_phase + 1) * delay;
1933 int half_phase = (num_phase / 2) * delay;
1935 element_info[i].explosion_delay = last_phase - 1;
1936 element_info[i].ignition_delay = half_phase;
1939 if (i == EL_BLACK_ORB)
1940 element_info[i].ignition_delay = 0;
1942 if (i == EL_BLACK_ORB)
1943 element_info[i].ignition_delay = 1;
1948 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1949 element_info[i].explosion_delay = 1;
1951 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1952 element_info[i].ignition_delay = 1;
1956 /* correct non-moving belts to start moving left */
1957 for (i = 0; i < NUM_BELTS; i++)
1958 if (game.belt_dir[i] == MV_NO_MOVING)
1959 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1961 /* check if any connected player was not found in playfield */
1962 for (i = 0; i < MAX_PLAYERS; i++)
1964 struct PlayerInfo *player = &stored_player[i];
1966 if (player->connected && !player->present)
1968 for (j = 0; j < MAX_PLAYERS; j++)
1970 struct PlayerInfo *some_player = &stored_player[j];
1971 int jx = some_player->jx, jy = some_player->jy;
1973 /* assign first free player found that is present in the playfield */
1974 if (some_player->present && !some_player->connected)
1976 player->present = TRUE;
1977 player->active = TRUE;
1979 some_player->present = FALSE;
1980 some_player->active = FALSE;
1983 player->element_nr = some_player->element_nr;
1986 #if USE_NEW_BLOCK_STYLE
1987 player->block_last_field = some_player->block_last_field;
1988 player->block_delay_adjustment = some_player->block_delay_adjustment;
1991 StorePlayer[jx][jy] = player->element_nr;
1992 player->jx = player->last_jx = jx;
1993 player->jy = player->last_jy = jy;
2003 /* when playing a tape, eliminate all players which do not participate */
2005 for (i = 0; i < MAX_PLAYERS; i++)
2007 if (stored_player[i].active && !tape.player_participates[i])
2009 struct PlayerInfo *player = &stored_player[i];
2010 int jx = player->jx, jy = player->jy;
2012 player->active = FALSE;
2013 StorePlayer[jx][jy] = 0;
2014 Feld[jx][jy] = EL_EMPTY;
2018 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2020 /* when in single player mode, eliminate all but the first active player */
2022 for (i = 0; i < MAX_PLAYERS; i++)
2024 if (stored_player[i].active)
2026 for (j = i + 1; j < MAX_PLAYERS; j++)
2028 if (stored_player[j].active)
2030 struct PlayerInfo *player = &stored_player[j];
2031 int jx = player->jx, jy = player->jy;
2033 player->active = FALSE;
2034 player->present = FALSE;
2036 StorePlayer[jx][jy] = 0;
2037 Feld[jx][jy] = EL_EMPTY;
2044 /* when recording the game, store which players take part in the game */
2047 for (i = 0; i < MAX_PLAYERS; i++)
2048 if (stored_player[i].active)
2049 tape.player_participates[i] = TRUE;
2054 for (i = 0; i < MAX_PLAYERS; i++)
2056 struct PlayerInfo *player = &stored_player[i];
2058 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2063 if (local_player == player)
2064 printf("Player %d is local player.\n", i+1);
2068 if (BorderElement == EL_EMPTY)
2071 SBX_Right = lev_fieldx - SCR_FIELDX;
2073 SBY_Lower = lev_fieldy - SCR_FIELDY;
2078 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2080 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2083 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2084 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2086 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2087 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2089 /* if local player not found, look for custom element that might create
2090 the player (make some assumptions about the right custom element) */
2091 if (!local_player->present)
2093 int start_x = 0, start_y = 0;
2094 int found_rating = 0;
2095 int found_element = EL_UNDEFINED;
2097 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2099 int element = Feld[x][y];
2104 if (!IS_CUSTOM_ELEMENT(element))
2107 if (CAN_CHANGE(element))
2109 for (i = 0; i < element_info[element].num_change_pages; i++)
2111 content = element_info[element].change_page[i].target_element;
2112 is_player = ELEM_IS_PLAYER(content);
2114 if (is_player && (found_rating < 3 || element < found_element))
2120 found_element = element;
2125 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2127 content = element_info[element].content[xx][yy];
2128 is_player = ELEM_IS_PLAYER(content);
2130 if (is_player && (found_rating < 2 || element < found_element))
2132 start_x = x + xx - 1;
2133 start_y = y + yy - 1;
2136 found_element = element;
2139 if (!CAN_CHANGE(element))
2142 for (i = 0; i < element_info[element].num_change_pages; i++)
2144 content= element_info[element].change_page[i].target_content[xx][yy];
2145 is_player = ELEM_IS_PLAYER(content);
2147 if (is_player && (found_rating < 1 || element < found_element))
2149 start_x = x + xx - 1;
2150 start_y = y + yy - 1;
2153 found_element = element;
2159 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2160 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2163 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2164 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2170 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2171 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2172 local_player->jx - MIDPOSX);
2174 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2175 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2176 local_player->jy - MIDPOSY);
2178 scroll_x = SBX_Left;
2179 scroll_y = SBY_Upper;
2180 if (local_player->jx >= SBX_Left + MIDPOSX)
2181 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2182 local_player->jx - MIDPOSX :
2184 if (local_player->jy >= SBY_Upper + MIDPOSY)
2185 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2186 local_player->jy - MIDPOSY :
2191 CloseDoor(DOOR_CLOSE_1);
2193 /* !!! FIX THIS (START) !!! */
2194 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2196 InitGameEngine_EM();
2203 /* after drawing the level, correct some elements */
2204 if (game.timegate_time_left == 0)
2205 CloseAllOpenTimegates();
2207 if (setup.soft_scrolling)
2208 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2210 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2213 /* !!! FIX THIS (END) !!! */
2215 /* copy default game door content to main double buffer */
2216 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2217 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2219 DrawGameDoorValues();
2223 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2224 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2225 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2229 /* copy actual game door content to door double buffer for OpenDoor() */
2230 BlitBitmap(drawto, bitmap_db_door,
2231 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2233 OpenDoor(DOOR_OPEN_ALL);
2235 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2237 if (setup.sound_music)
2240 KeyboardAutoRepeatOffUnlessAutoplay();
2244 for (i = 0; i < MAX_PLAYERS; i++)
2245 printf("Player %d %sactive.\n",
2246 i + 1, (stored_player[i].active ? "" : "not "));
2250 printf("::: starting game [%d]\n", FrameCounter);
2254 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2256 /* this is used for non-R'n'D game engines to update certain engine values */
2258 /* needed to determine if sounds are played within the visible screen area */
2259 scroll_x = actual_scroll_x;
2260 scroll_y = actual_scroll_y;
2263 void InitMovDir(int x, int y)
2265 int i, element = Feld[x][y];
2266 static int xy[4][2] =
2273 static int direction[3][4] =
2275 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2276 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2277 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2286 Feld[x][y] = EL_BUG;
2287 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2290 case EL_SPACESHIP_RIGHT:
2291 case EL_SPACESHIP_UP:
2292 case EL_SPACESHIP_LEFT:
2293 case EL_SPACESHIP_DOWN:
2294 Feld[x][y] = EL_SPACESHIP;
2295 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2298 case EL_BD_BUTTERFLY_RIGHT:
2299 case EL_BD_BUTTERFLY_UP:
2300 case EL_BD_BUTTERFLY_LEFT:
2301 case EL_BD_BUTTERFLY_DOWN:
2302 Feld[x][y] = EL_BD_BUTTERFLY;
2303 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2306 case EL_BD_FIREFLY_RIGHT:
2307 case EL_BD_FIREFLY_UP:
2308 case EL_BD_FIREFLY_LEFT:
2309 case EL_BD_FIREFLY_DOWN:
2310 Feld[x][y] = EL_BD_FIREFLY;
2311 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2314 case EL_PACMAN_RIGHT:
2316 case EL_PACMAN_LEFT:
2317 case EL_PACMAN_DOWN:
2318 Feld[x][y] = EL_PACMAN;
2319 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2322 case EL_SP_SNIKSNAK:
2323 MovDir[x][y] = MV_UP;
2326 case EL_SP_ELECTRON:
2327 MovDir[x][y] = MV_LEFT;
2334 Feld[x][y] = EL_MOLE;
2335 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2339 if (IS_CUSTOM_ELEMENT(element))
2341 struct ElementInfo *ei = &element_info[element];
2342 int move_direction_initial = ei->move_direction_initial;
2343 int move_pattern = ei->move_pattern;
2345 if (move_direction_initial == MV_START_PREVIOUS)
2347 if (MovDir[x][y] != MV_NO_MOVING)
2350 move_direction_initial = MV_START_AUTOMATIC;
2353 if (move_direction_initial == MV_START_RANDOM)
2354 MovDir[x][y] = 1 << RND(4);
2355 else if (move_direction_initial & MV_ANY_DIRECTION)
2356 MovDir[x][y] = move_direction_initial;
2357 else if (move_pattern == MV_ALL_DIRECTIONS ||
2358 move_pattern == MV_TURNING_LEFT ||
2359 move_pattern == MV_TURNING_RIGHT ||
2360 move_pattern == MV_TURNING_LEFT_RIGHT ||
2361 move_pattern == MV_TURNING_RIGHT_LEFT ||
2362 move_pattern == MV_TURNING_RANDOM)
2363 MovDir[x][y] = 1 << RND(4);
2364 else if (move_pattern == MV_HORIZONTAL)
2365 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2366 else if (move_pattern == MV_VERTICAL)
2367 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2368 else if (move_pattern & MV_ANY_DIRECTION)
2369 MovDir[x][y] = element_info[element].move_pattern;
2370 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2371 move_pattern == MV_ALONG_RIGHT_SIDE)
2374 /* use random direction as default start direction */
2375 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2376 MovDir[x][y] = 1 << RND(4);
2379 for (i = 0; i < NUM_DIRECTIONS; i++)
2381 int x1 = x + xy[i][0];
2382 int y1 = y + xy[i][1];
2384 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2386 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2387 MovDir[x][y] = direction[0][i];
2389 MovDir[x][y] = direction[1][i];
2398 MovDir[x][y] = 1 << RND(4);
2400 if (element != EL_BUG &&
2401 element != EL_SPACESHIP &&
2402 element != EL_BD_BUTTERFLY &&
2403 element != EL_BD_FIREFLY)
2406 for (i = 0; i < NUM_DIRECTIONS; i++)
2408 int x1 = x + xy[i][0];
2409 int y1 = y + xy[i][1];
2411 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2413 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2415 MovDir[x][y] = direction[0][i];
2418 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2419 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2421 MovDir[x][y] = direction[1][i];
2430 GfxDir[x][y] = MovDir[x][y];
2433 void InitAmoebaNr(int x, int y)
2436 int group_nr = AmoebeNachbarNr(x, y);
2440 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2442 if (AmoebaCnt[i] == 0)
2450 AmoebaNr[x][y] = group_nr;
2451 AmoebaCnt[group_nr]++;
2452 AmoebaCnt2[group_nr]++;
2458 boolean raise_level = FALSE;
2460 if (local_player->MovPos)
2464 if (tape.auto_play) /* tape might already be stopped here */
2465 tape.auto_play_level_solved = TRUE;
2467 if (tape.playing && tape.auto_play)
2468 tape.auto_play_level_solved = TRUE;
2471 local_player->LevelSolved = FALSE;
2473 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2477 if (!tape.playing && setup.sound_loops)
2478 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2479 SND_CTRL_PLAY_LOOP);
2481 while (TimeLeft > 0)
2483 if (!tape.playing && !setup.sound_loops)
2484 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2485 if (TimeLeft > 0 && !(TimeLeft % 10))
2486 RaiseScore(level.score[SC_TIME_BONUS]);
2487 if (TimeLeft > 100 && !(TimeLeft % 10))
2492 DrawGameValue_Time(TimeLeft);
2500 if (!tape.playing && setup.sound_loops)
2501 StopSound(SND_GAME_LEVELTIME_BONUS);
2503 else if (level.time == 0) /* level without time limit */
2505 if (!tape.playing && setup.sound_loops)
2506 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2507 SND_CTRL_PLAY_LOOP);
2509 while (TimePlayed < 999)
2511 if (!tape.playing && !setup.sound_loops)
2512 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2513 if (TimePlayed < 999 && !(TimePlayed % 10))
2514 RaiseScore(level.score[SC_TIME_BONUS]);
2515 if (TimePlayed < 900 && !(TimePlayed % 10))
2520 DrawGameValue_Time(TimePlayed);
2528 if (!tape.playing && setup.sound_loops)
2529 StopSound(SND_GAME_LEVELTIME_BONUS);
2532 /* close exit door after last player */
2533 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2534 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2535 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2537 int element = Feld[ExitX][ExitY];
2539 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2540 EL_SP_EXIT_CLOSING);
2542 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2545 /* Hero disappears */
2546 if (ExitX >= 0 && ExitY >= 0)
2547 DrawLevelField(ExitX, ExitY);
2554 CloseDoor(DOOR_CLOSE_1);
2559 SaveTape(tape.level_nr); /* Ask to save tape */
2562 if (level_nr == leveldir_current->handicap_level)
2564 leveldir_current->handicap_level++;
2565 SaveLevelSetup_SeriesInfo();
2568 if (level_editor_test_game)
2569 local_player->score = -1; /* no highscore when playing from editor */
2570 else if (level_nr < leveldir_current->last_level)
2571 raise_level = TRUE; /* advance to next level */
2573 if ((hi_pos = NewHiScore()) >= 0)
2575 game_status = GAME_MODE_SCORES;
2576 DrawHallOfFame(hi_pos);
2585 game_status = GAME_MODE_MAIN;
2602 LoadScore(level_nr);
2604 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2605 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2608 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2610 if (local_player->score > highscore[k].Score)
2612 /* player has made it to the hall of fame */
2614 if (k < MAX_SCORE_ENTRIES - 1)
2616 int m = MAX_SCORE_ENTRIES - 1;
2619 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2620 if (!strcmp(setup.player_name, highscore[l].Name))
2622 if (m == k) /* player's new highscore overwrites his old one */
2626 for (l = m; l > k; l--)
2628 strcpy(highscore[l].Name, highscore[l - 1].Name);
2629 highscore[l].Score = highscore[l - 1].Score;
2636 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2637 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2638 highscore[k].Score = local_player->score;
2644 else if (!strncmp(setup.player_name, highscore[k].Name,
2645 MAX_PLAYER_NAME_LEN))
2646 break; /* player already there with a higher score */
2652 SaveScore(level_nr);
2657 inline static int getElementMoveStepsize(int x, int y)
2659 int element = Feld[x][y];
2660 int direction = MovDir[x][y];
2661 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2662 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2663 int horiz_move = (dx != 0);
2664 int sign = (horiz_move ? dx : dy);
2665 int step = sign * element_info[element].move_stepsize;
2667 /* special values for move stepsize for spring and things on conveyor belt */
2671 if (element == EL_SPRING)
2672 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2673 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2674 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2675 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2677 if (CAN_FALL(element) &&
2678 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2679 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2680 else if (element == EL_SPRING)
2681 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2688 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2690 if (player->GfxAction != action || player->GfxDir != dir)
2693 printf("Player frame reset! (%d => %d, %d => %d)\n",
2694 player->GfxAction, action, player->GfxDir, dir);
2697 player->GfxAction = action;
2698 player->GfxDir = dir;
2700 player->StepFrame = 0;
2704 static void ResetRandomAnimationValue(int x, int y)
2706 GfxRandom[x][y] = INIT_GFX_RANDOM();
2709 static void ResetGfxAnimation(int x, int y)
2712 GfxAction[x][y] = ACTION_DEFAULT;
2713 GfxDir[x][y] = MovDir[x][y];
2716 void InitMovingField(int x, int y, int direction)
2718 int element = Feld[x][y];
2719 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2720 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2724 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2725 ResetGfxAnimation(x, y);
2727 #if USE_CAN_MOVE_NOT_MOVING
2729 MovDir[x][y] = direction;
2730 GfxDir[x][y] = direction;
2731 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2732 ACTION_FALLING : ACTION_MOVING);
2734 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2736 if (Feld[newx][newy] == EL_EMPTY)
2737 Feld[newx][newy] = EL_BLOCKED;
2739 MovDir[newx][newy] = MovDir[x][y];
2740 GfxFrame[newx][newy] = GfxFrame[x][y];
2741 GfxRandom[newx][newy] = GfxRandom[x][y];
2742 GfxAction[newx][newy] = GfxAction[x][y];
2743 GfxDir[newx][newy] = GfxDir[x][y];
2748 MovDir[newx][newy] = MovDir[x][y] = direction;
2749 GfxDir[x][y] = direction;
2751 if (Feld[newx][newy] == EL_EMPTY)
2752 Feld[newx][newy] = EL_BLOCKED;
2754 if (direction == MV_DOWN && CAN_FALL(element))
2755 GfxAction[x][y] = ACTION_FALLING;
2757 GfxAction[x][y] = ACTION_MOVING;
2759 GfxFrame[newx][newy] = GfxFrame[x][y];
2760 GfxRandom[newx][newy] = GfxRandom[x][y];
2761 GfxAction[newx][newy] = GfxAction[x][y];
2762 GfxDir[newx][newy] = GfxDir[x][y];
2766 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2768 int direction = MovDir[x][y];
2769 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2770 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2776 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2778 int oldx = x, oldy = y;
2779 int direction = MovDir[x][y];
2781 if (direction == MV_LEFT)
2783 else if (direction == MV_RIGHT)
2785 else if (direction == MV_UP)
2787 else if (direction == MV_DOWN)
2790 *comes_from_x = oldx;
2791 *comes_from_y = oldy;
2794 int MovingOrBlocked2Element(int x, int y)
2796 int element = Feld[x][y];
2798 if (element == EL_BLOCKED)
2802 Blocked2Moving(x, y, &oldx, &oldy);
2803 return Feld[oldx][oldy];
2809 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2811 /* like MovingOrBlocked2Element(), but if element is moving
2812 and (x,y) is the field the moving element is just leaving,
2813 return EL_BLOCKED instead of the element value */
2814 int element = Feld[x][y];
2816 if (IS_MOVING(x, y))
2818 if (element == EL_BLOCKED)
2822 Blocked2Moving(x, y, &oldx, &oldy);
2823 return Feld[oldx][oldy];
2832 static void RemoveField(int x, int y)
2834 Feld[x][y] = EL_EMPTY;
2841 ChangeDelay[x][y] = 0;
2842 ChangePage[x][y] = -1;
2843 Pushed[x][y] = FALSE;
2846 ExplodeField[x][y] = EX_TYPE_NONE;
2849 GfxElement[x][y] = EL_UNDEFINED;
2850 GfxAction[x][y] = ACTION_DEFAULT;
2851 GfxDir[x][y] = MV_NO_MOVING;
2854 void RemoveMovingField(int x, int y)
2856 int oldx = x, oldy = y, newx = x, newy = y;
2857 int element = Feld[x][y];
2858 int next_element = EL_UNDEFINED;
2860 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2863 if (IS_MOVING(x, y))
2865 Moving2Blocked(x, y, &newx, &newy);
2867 if (Feld[newx][newy] != EL_BLOCKED)
2870 if (Feld[newx][newy] != EL_BLOCKED)
2872 /* element is moving, but target field is not free (blocked), but
2873 already occupied by something different (example: acid pool);
2874 in this case, only remove the moving field, but not the target */
2876 RemoveField(oldx, oldy);
2878 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2880 DrawLevelField(oldx, oldy);
2886 else if (element == EL_BLOCKED)
2888 Blocked2Moving(x, y, &oldx, &oldy);
2889 if (!IS_MOVING(oldx, oldy))
2893 if (element == EL_BLOCKED &&
2894 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2895 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2896 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2897 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2898 next_element = get_next_element(Feld[oldx][oldy]);
2900 RemoveField(oldx, oldy);
2901 RemoveField(newx, newy);
2903 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2905 if (next_element != EL_UNDEFINED)
2906 Feld[oldx][oldy] = next_element;
2908 DrawLevelField(oldx, oldy);
2909 DrawLevelField(newx, newy);
2912 void DrawDynamite(int x, int y)
2914 int sx = SCREENX(x), sy = SCREENY(y);
2915 int graphic = el2img(Feld[x][y]);
2918 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2921 if (IS_WALKABLE_INSIDE(Back[x][y]))
2925 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2926 else if (Store[x][y])
2927 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2929 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2932 if (Back[x][y] || Store[x][y])
2933 DrawGraphicThruMask(sx, sy, graphic, frame);
2935 DrawGraphic(sx, sy, graphic, frame);
2937 if (game.emulation == EMU_SUPAPLEX)
2938 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2939 else if (Store[x][y])
2940 DrawGraphicThruMask(sx, sy, graphic, frame);
2942 DrawGraphic(sx, sy, graphic, frame);
2946 void CheckDynamite(int x, int y)
2948 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2952 if (MovDelay[x][y] != 0)
2955 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2962 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2964 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2965 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2966 StopSound(SND_DYNAMITE_ACTIVE);
2968 StopSound(SND_DYNABOMB_ACTIVE);
2974 void DrawRelocatePlayer(struct PlayerInfo *player)
2976 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2977 boolean no_delay = (tape.warp_forward);
2978 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2979 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2980 int jx = player->jx;
2981 int jy = player->jy;
2983 if (level.instant_relocation)
2986 int offset = (setup.scroll_delay ? 3 : 0);
2988 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2990 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2991 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2992 local_player->jx - MIDPOSX);
2994 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2995 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2996 local_player->jy - MIDPOSY);
3000 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3001 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3002 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3004 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3005 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3006 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3008 /* don't scroll over playfield boundaries */
3009 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3010 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3012 /* don't scroll over playfield boundaries */
3013 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3014 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3017 scroll_x += (local_player->jx - old_jx);
3018 scroll_y += (local_player->jy - old_jy);
3020 /* don't scroll over playfield boundaries */
3021 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3022 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3024 /* don't scroll over playfield boundaries */
3025 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3026 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3029 RedrawPlayfield(TRUE, 0,0,0,0);
3035 int offset = (setup.scroll_delay ? 3 : 0);
3037 int scroll_xx = -999, scroll_yy = -999;
3039 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3041 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3044 int fx = FX, fy = FY;
3046 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3047 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3048 local_player->jx - MIDPOSX);
3050 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3051 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3052 local_player->jy - MIDPOSY);
3054 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3055 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3058 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3061 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3068 fx += dx * TILEX / 2;
3069 fy += dy * TILEY / 2;
3071 ScrollLevel(dx, dy);
3074 /* scroll in two steps of half tile size to make things smoother */
3075 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3077 Delay(wait_delay_value);
3079 /* scroll second step to align at full tile size */
3081 Delay(wait_delay_value);
3084 int scroll_xx = -999, scroll_yy = -999;
3086 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3088 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3091 int fx = FX, fy = FY;
3093 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3094 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3095 local_player->jx - MIDPOSX);
3097 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3098 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3099 local_player->jy - MIDPOSY);
3101 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3102 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3105 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3108 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3115 fx += dx * TILEX / 2;
3116 fy += dy * TILEY / 2;
3118 ScrollLevel(dx, dy);
3121 /* scroll in two steps of half tile size to make things smoother */
3122 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3124 Delay(wait_delay_value);
3126 /* scroll second step to align at full tile size */
3128 Delay(wait_delay_value);
3134 Delay(wait_delay_value);
3138 void RelocatePlayer(int jx, int jy, int el_player_raw)
3141 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3143 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3145 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3146 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3147 boolean no_delay = (tape.warp_forward);
3148 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3149 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3150 int old_jx = player->jx;
3151 int old_jy = player->jy;
3152 int old_element = Feld[old_jx][old_jy];
3153 int element = Feld[jx][jy];
3154 boolean player_relocated = (old_jx != jx || old_jy != jy);
3156 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3157 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3159 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3160 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3161 int leave_side_horiz = move_dir_horiz;
3162 int leave_side_vert = move_dir_vert;
3164 static int trigger_sides[4][2] =
3166 /* enter side leave side */
3167 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3168 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3169 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3170 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3172 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3173 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3174 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3175 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3177 int enter_side = enter_side_horiz | enter_side_vert;
3178 int leave_side = leave_side_horiz | leave_side_vert;
3180 if (player->GameOver) /* do not reanimate dead player */
3183 if (!player_relocated) /* no need to relocate the player */
3186 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3188 RemoveField(jx, jy); /* temporarily remove newly placed player */
3189 DrawLevelField(jx, jy);
3192 if (player->present)
3194 while (player->MovPos)
3196 ScrollPlayer(player, SCROLL_GO_ON);
3197 ScrollScreen(NULL, SCROLL_GO_ON);
3199 #if USE_NEW_MOVE_DELAY
3200 AdvanceFrameAndPlayerCounters(player->index_nr);
3208 Delay(wait_delay_value);
3211 DrawPlayer(player); /* needed here only to cleanup last field */
3212 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3214 player->is_moving = FALSE;
3218 if (IS_CUSTOM_ELEMENT(old_element))
3219 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3221 player->index_bit, leave_side);
3223 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3225 player->index_bit, leave_side);
3228 Feld[jx][jy] = el_player;
3229 InitPlayerField(jx, jy, el_player, TRUE);
3231 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3233 Feld[jx][jy] = element;
3234 InitField(jx, jy, FALSE);
3238 if (player == local_player) /* only visually relocate local player */
3239 DrawRelocatePlayer(player);
3243 TestIfHeroTouchesBadThing(jx, jy);
3244 TestIfPlayerTouchesCustomElement(jx, jy);
3248 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3253 /* needed to allow change of walkable custom element by entering player */
3254 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3255 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3257 /* needed to allow change of walkable custom element by entering player */
3258 Changed[jx][jy] = 0; /* allow another change */
3263 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3264 enter_side == MV_LEFT ? "left" :
3265 enter_side == MV_RIGHT ? "right" :
3266 enter_side == MV_UP ? "top" :
3267 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3271 if (IS_CUSTOM_ELEMENT(element))
3272 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3273 player->index_bit, enter_side);
3275 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3277 player->index_bit, enter_side);
3281 void Explode(int ex, int ey, int phase, int mode)
3288 /* !!! eliminate this variable !!! */
3289 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3294 int last_phase = num_phase * delay;
3295 int half_phase = (num_phase / 2) * delay;
3296 int first_phase_after_start = EX_PHASE_START + 1;
3300 if (game.explosions_delayed)
3302 ExplodeField[ex][ey] = mode;
3306 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3308 int center_element = Feld[ex][ey];
3311 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3315 /* --- This is only really needed (and now handled) in "Impact()". --- */
3316 /* do not explode moving elements that left the explode field in time */
3317 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3318 center_element == EL_EMPTY &&
3319 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3324 if (mode == EX_TYPE_NORMAL ||
3325 mode == EX_TYPE_CENTER ||
3326 mode == EX_TYPE_CROSS)
3327 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3329 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3330 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3333 /* remove things displayed in background while burning dynamite */
3334 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3337 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3339 /* put moving element to center field (and let it explode there) */
3340 center_element = MovingOrBlocked2Element(ex, ey);
3341 RemoveMovingField(ex, ey);
3342 Feld[ex][ey] = center_element;
3348 last_phase = element_info[center_element].explosion_delay + 1;
3350 last_phase = element_info[center_element].explosion_delay;
3354 printf("::: %d -> %d\n", center_element, last_phase);
3358 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3360 int xx = x - ex + 1;
3361 int yy = y - ey + 1;
3366 if (!IN_LEV_FIELD(x, y) ||
3367 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3368 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3371 if (!IN_LEV_FIELD(x, y) ||
3372 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3376 if (!IN_LEV_FIELD(x, y) ||
3377 ((mode != EX_TYPE_NORMAL ||
3378 center_element == EL_AMOEBA_TO_DIAMOND) &&
3379 (x != ex || y != ey)))
3383 element = Feld[x][y];
3385 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3387 element = MovingOrBlocked2Element(x, y);
3389 if (!IS_EXPLOSION_PROOF(element))
3390 RemoveMovingField(x, y);
3396 if (IS_EXPLOSION_PROOF(element))
3399 /* indestructible elements can only explode in center (but not flames) */
3401 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3402 mode == EX_TYPE_BORDER)) ||
3403 element == EL_FLAMES)
3406 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3407 element == EL_FLAMES)
3413 if ((IS_INDESTRUCTIBLE(element) &&
3414 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3415 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3416 element == EL_FLAMES)
3420 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3421 behaviour, for example when touching a yamyam that explodes to rocks
3422 with active deadly shield, a rock is created under the player !!! */
3423 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3425 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3426 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3427 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3429 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3432 if (IS_ACTIVE_BOMB(element))
3434 /* re-activate things under the bomb like gate or penguin */
3436 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3439 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3444 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3445 element_info[Feld[x][y]].token_name,
3446 Store[x][y], Store2[x][y]);
3453 /* save walkable background elements while explosion on same tile */
3455 if (IS_INDESTRUCTIBLE(element))
3456 Back[x][y] = element;
3460 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3461 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3462 Back[x][y] = element;
3464 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3465 (x != ex || y != ey))
3466 Back[x][y] = element;
3469 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3470 Back[x][y] = element;
3474 /* ignite explodable elements reached by other explosion */
3475 if (element == EL_EXPLOSION)
3476 element = Store2[x][y];
3479 if (AmoebaNr[x][y] &&
3480 (element == EL_AMOEBA_FULL ||
3481 element == EL_BD_AMOEBA ||
3482 element == EL_AMOEBA_GROWING))
3484 AmoebaCnt[AmoebaNr[x][y]]--;
3485 AmoebaCnt2[AmoebaNr[x][y]]--;
3491 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3493 switch(StorePlayer[ex][ey])
3496 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3499 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3502 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3506 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3511 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3512 Store[x][y] = EL_EMPTY;
3514 if (game.emulation == EMU_SUPAPLEX)
3515 Store[x][y] = EL_EMPTY;
3518 else if (center_element == EL_MOLE)
3519 Store[x][y] = EL_EMERALD_RED;
3520 else if (center_element == EL_PENGUIN)
3521 Store[x][y] = EL_EMERALD_PURPLE;
3522 else if (center_element == EL_BUG)
3523 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3524 else if (center_element == EL_BD_BUTTERFLY)
3525 Store[x][y] = EL_BD_DIAMOND;
3526 else if (center_element == EL_SP_ELECTRON)
3527 Store[x][y] = EL_SP_INFOTRON;
3528 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3529 Store[x][y] = level.amoeba_content;
3530 else if (center_element == EL_YAMYAM)
3531 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3532 else if (IS_CUSTOM_ELEMENT(center_element) &&
3533 element_info[center_element].content[xx][yy] != EL_EMPTY)
3534 Store[x][y] = element_info[center_element].content[xx][yy];
3535 else if (element == EL_WALL_EMERALD)
3536 Store[x][y] = EL_EMERALD;
3537 else if (element == EL_WALL_DIAMOND)
3538 Store[x][y] = EL_DIAMOND;
3539 else if (element == EL_WALL_BD_DIAMOND)
3540 Store[x][y] = EL_BD_DIAMOND;
3541 else if (element == EL_WALL_EMERALD_YELLOW)
3542 Store[x][y] = EL_EMERALD_YELLOW;
3543 else if (element == EL_WALL_EMERALD_RED)
3544 Store[x][y] = EL_EMERALD_RED;
3545 else if (element == EL_WALL_EMERALD_PURPLE)
3546 Store[x][y] = EL_EMERALD_PURPLE;
3547 else if (element == EL_WALL_PEARL)
3548 Store[x][y] = EL_PEARL;
3549 else if (element == EL_WALL_CRYSTAL)
3550 Store[x][y] = EL_CRYSTAL;
3551 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3552 Store[x][y] = element_info[element].content[1][1];
3554 Store[x][y] = EL_EMPTY;
3556 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3557 center_element == EL_AMOEBA_TO_DIAMOND)
3558 Store2[x][y] = element;
3561 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3562 element_info[Store2[x][y]].token_name);
3566 if (AmoebaNr[x][y] &&
3567 (element == EL_AMOEBA_FULL ||
3568 element == EL_BD_AMOEBA ||
3569 element == EL_AMOEBA_GROWING))
3571 AmoebaCnt[AmoebaNr[x][y]]--;
3572 AmoebaCnt2[AmoebaNr[x][y]]--;
3578 MovDir[x][y] = MovPos[x][y] = 0;
3579 GfxDir[x][y] = MovDir[x][y];
3584 Feld[x][y] = EL_EXPLOSION;
3586 GfxElement[x][y] = center_element;
3588 GfxElement[x][y] = EL_UNDEFINED;
3591 ExplodePhase[x][y] = 1;
3593 ExplodeDelay[x][y] = last_phase;
3598 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3600 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3607 if (center_element == EL_YAMYAM)
3608 game.yamyam_content_nr =
3609 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3612 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3613 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3627 GfxFrame[x][y] = 0; /* restart explosion animation */
3631 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3635 last_phase = ExplodeDelay[x][y];
3638 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3642 /* activate this even in non-DEBUG version until cause for crash in
3643 getGraphicAnimationFrame() (see below) is found and eliminated */
3647 if (GfxElement[x][y] == EL_UNDEFINED)
3650 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3651 printf("Explode(): This should never happen!\n");
3654 GfxElement[x][y] = EL_EMPTY;
3660 border_element = Store2[x][y];
3662 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3663 border_element = StorePlayer[x][y];
3665 if (IS_PLAYER(x, y))
3666 border_element = StorePlayer[x][y];
3670 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3671 element_info[border_element].token_name, Store2[x][y]);
3675 printf("::: phase == %d\n", phase);
3678 if (phase == element_info[border_element].ignition_delay ||
3679 phase == last_phase)
3681 boolean border_explosion = FALSE;
3685 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3686 !PLAYER_EXPLOSION_PROTECTED(x, y))
3688 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3691 if (IS_PLAYER(x, y))
3694 KillHeroUnlessExplosionProtected(x, y);
3695 border_explosion = TRUE;
3698 if (phase == last_phase)
3699 printf("::: IS_PLAYER\n");
3702 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3705 printf("::: %d,%d: %d %s\n", x, y, border_element,
3706 element_info[border_element].token_name);
3709 Feld[x][y] = Store2[x][y];
3712 border_explosion = TRUE;
3715 if (phase == last_phase)
3716 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3719 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3721 AmoebeUmwandeln(x, y);
3723 border_explosion = TRUE;
3726 if (phase == last_phase)
3727 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3728 element_info[border_element].explosion_delay,
3729 element_info[border_element].ignition_delay,
3735 /* if an element just explodes due to another explosion (chain-reaction),
3736 do not immediately end the new explosion when it was the last frame of
3737 the explosion (as it would be done in the following "if"-statement!) */
3738 if (border_explosion && phase == last_phase)
3745 if (phase == first_phase_after_start)
3747 int element = Store2[x][y];
3749 if (element == EL_BLACK_ORB)
3751 Feld[x][y] = Store2[x][y];
3756 else if (phase == half_phase)
3758 int element = Store2[x][y];
3760 if (IS_PLAYER(x, y))
3761 KillHeroUnlessExplosionProtected(x, y);
3762 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3764 Feld[x][y] = Store2[x][y];
3768 else if (element == EL_AMOEBA_TO_DIAMOND)
3769 AmoebeUmwandeln(x, y);
3773 if (phase == last_phase)
3778 printf("::: done: phase == %d\n", phase);
3782 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3785 element = Feld[x][y] = Store[x][y];
3786 Store[x][y] = Store2[x][y] = 0;
3787 GfxElement[x][y] = EL_UNDEFINED;
3789 /* player can escape from explosions and might therefore be still alive */
3790 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3791 element <= EL_PLAYER_IS_EXPLODING_4)
3792 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3794 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3795 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3796 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3799 /* restore probably existing indestructible background element */
3800 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3801 element = Feld[x][y] = Back[x][y];
3804 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3805 GfxDir[x][y] = MV_NO_MOVING;
3806 ChangeDelay[x][y] = 0;
3807 ChangePage[x][y] = -1;
3810 InitField_WithBug2(x, y, FALSE);
3812 InitField(x, y, FALSE);
3814 /* !!! not needed !!! */
3816 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3817 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3820 if (CAN_MOVE(element))
3825 DrawLevelField(x, y);
3827 TestIfElementTouchesCustomElement(x, y);
3829 if (GFX_CRUMBLED(element))
3830 DrawLevelFieldCrumbledSandNeighbours(x, y);
3832 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3833 StorePlayer[x][y] = 0;
3835 if (ELEM_IS_PLAYER(element))
3836 RelocatePlayer(x, y, element);
3839 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3841 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3845 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3847 int stored = Store[x][y];
3848 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3849 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3853 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3855 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3859 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3863 printf("::: %d / %d [%d - %d]\n",
3864 GfxFrame[x][y], phase - delay, phase, delay);
3868 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3869 element_info[GfxElement[x][y]].token_name,
3874 DrawLevelFieldCrumbledSand(x, y);
3876 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3878 DrawLevelElement(x, y, Back[x][y]);
3879 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3881 else if (IS_WALKABLE_UNDER(Back[x][y]))
3883 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3884 DrawLevelElementThruMask(x, y, Back[x][y]);
3886 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3887 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3891 void DynaExplode(int ex, int ey)
3894 int dynabomb_element = Feld[ex][ey];
3895 int dynabomb_size = 1;
3896 boolean dynabomb_xl = FALSE;
3897 struct PlayerInfo *player;
3898 static int xy[4][2] =
3906 if (IS_ACTIVE_BOMB(dynabomb_element))
3908 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3909 dynabomb_size = player->dynabomb_size;
3910 dynabomb_xl = player->dynabomb_xl;
3911 player->dynabombs_left++;
3914 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3916 for (i = 0; i < NUM_DIRECTIONS; i++)
3918 for (j = 1; j <= dynabomb_size; j++)
3920 int x = ex + j * xy[i][0];
3921 int y = ey + j * xy[i][1];
3924 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3927 element = Feld[x][y];
3929 /* do not restart explosions of fields with active bombs */
3930 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3933 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3937 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3938 !IS_DIGGABLE(element) && !dynabomb_xl)
3941 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3942 !CAN_GROW_INTO(element) && !dynabomb_xl)
3946 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3947 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3948 element != EL_SAND && !dynabomb_xl)
3955 void Bang(int x, int y)
3958 int element = MovingOrBlocked2Element(x, y);
3960 int element = Feld[x][y];
3964 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3966 if (IS_PLAYER(x, y))
3969 struct PlayerInfo *player = PLAYERINFO(x, y);
3971 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3972 player->element_nr);
3977 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3979 if (game.emulation == EMU_SUPAPLEX)
3980 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3982 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3987 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3995 case EL_BD_BUTTERFLY:
3998 case EL_DARK_YAMYAM:
4002 RaiseScoreElement(element);
4003 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4005 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4006 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4007 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4008 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4009 case EL_DYNABOMB_INCREASE_NUMBER:
4010 case EL_DYNABOMB_INCREASE_SIZE:
4011 case EL_DYNABOMB_INCREASE_POWER:
4016 case EL_LAMP_ACTIVE:
4018 case EL_AMOEBA_TO_DIAMOND:
4020 if (IS_PLAYER(x, y))
4021 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4023 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4027 if (element_info[element].explosion_type == EXPLODES_CROSS)
4029 if (CAN_EXPLODE_CROSS(element))
4032 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4037 else if (element_info[element].explosion_type == EXPLODES_1X1)
4039 else if (CAN_EXPLODE_1X1(element))
4041 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4043 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4047 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4050 void SplashAcid(int x, int y)
4053 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4054 (!IN_LEV_FIELD(x - 1, y - 2) ||
4055 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4056 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4058 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4059 (!IN_LEV_FIELD(x + 1, y - 2) ||
4060 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4061 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4063 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4065 /* input: position of element entering acid (obsolete) */
4067 int element = Feld[x][y];
4069 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4072 if (element != EL_ACID_SPLASH_LEFT &&
4073 element != EL_ACID_SPLASH_RIGHT)
4075 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4077 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4078 (!IN_LEV_FIELD(x - 1, y - 1) ||
4079 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4080 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4082 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4083 (!IN_LEV_FIELD(x + 1, y - 1) ||
4084 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4085 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4090 static void InitBeltMovement()
4092 static int belt_base_element[4] =
4094 EL_CONVEYOR_BELT_1_LEFT,
4095 EL_CONVEYOR_BELT_2_LEFT,
4096 EL_CONVEYOR_BELT_3_LEFT,
4097 EL_CONVEYOR_BELT_4_LEFT
4099 static int belt_base_active_element[4] =
4101 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4102 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4103 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4104 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4109 /* set frame order for belt animation graphic according to belt direction */
4110 for (i = 0; i < NUM_BELTS; i++)
4114 for (j = 0; j < NUM_BELT_PARTS; j++)
4116 int element = belt_base_active_element[belt_nr] + j;
4117 int graphic = el2img(element);
4119 if (game.belt_dir[i] == MV_LEFT)
4120 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4122 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4126 for (y = 0; y < lev_fieldy; y++)
4128 for (x = 0; x < lev_fieldx; x++)
4130 int element = Feld[x][y];
4132 for (i = 0; i < NUM_BELTS; i++)
4134 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4136 int e_belt_nr = getBeltNrFromBeltElement(element);
4139 if (e_belt_nr == belt_nr)
4141 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4143 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4151 static void ToggleBeltSwitch(int x, int y)
4153 static int belt_base_element[4] =
4155 EL_CONVEYOR_BELT_1_LEFT,
4156 EL_CONVEYOR_BELT_2_LEFT,
4157 EL_CONVEYOR_BELT_3_LEFT,
4158 EL_CONVEYOR_BELT_4_LEFT
4160 static int belt_base_active_element[4] =
4162 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4163 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4164 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4165 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4167 static int belt_base_switch_element[4] =
4169 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4170 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4171 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4172 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4174 static int belt_move_dir[4] =
4182 int element = Feld[x][y];
4183 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4184 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4185 int belt_dir = belt_move_dir[belt_dir_nr];
4188 if (!IS_BELT_SWITCH(element))
4191 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4192 game.belt_dir[belt_nr] = belt_dir;
4194 if (belt_dir_nr == 3)
4197 /* set frame order for belt animation graphic according to belt direction */
4198 for (i = 0; i < NUM_BELT_PARTS; i++)
4200 int element = belt_base_active_element[belt_nr] + i;
4201 int graphic = el2img(element);
4203 if (belt_dir == MV_LEFT)
4204 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4206 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4209 for (yy = 0; yy < lev_fieldy; yy++)
4211 for (xx = 0; xx < lev_fieldx; xx++)
4213 int element = Feld[xx][yy];
4215 if (IS_BELT_SWITCH(element))
4217 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4219 if (e_belt_nr == belt_nr)
4221 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4222 DrawLevelField(xx, yy);
4225 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4227 int e_belt_nr = getBeltNrFromBeltElement(element);
4229 if (e_belt_nr == belt_nr)
4231 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4233 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4234 DrawLevelField(xx, yy);
4237 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4239 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4241 if (e_belt_nr == belt_nr)
4243 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4245 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4246 DrawLevelField(xx, yy);
4253 static void ToggleSwitchgateSwitch(int x, int y)
4257 game.switchgate_pos = !game.switchgate_pos;
4259 for (yy = 0; yy < lev_fieldy; yy++)
4261 for (xx = 0; xx < lev_fieldx; xx++)
4263 int element = Feld[xx][yy];
4265 if (element == EL_SWITCHGATE_SWITCH_UP ||
4266 element == EL_SWITCHGATE_SWITCH_DOWN)
4268 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4269 DrawLevelField(xx, yy);
4271 else if (element == EL_SWITCHGATE_OPEN ||
4272 element == EL_SWITCHGATE_OPENING)
4274 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4276 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4278 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4281 else if (element == EL_SWITCHGATE_CLOSED ||
4282 element == EL_SWITCHGATE_CLOSING)
4284 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4286 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4288 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4295 static int getInvisibleActiveFromInvisibleElement(int element)
4297 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4298 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4299 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4303 static int getInvisibleFromInvisibleActiveElement(int element)
4305 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4306 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4307 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4311 static void RedrawAllLightSwitchesAndInvisibleElements()
4315 for (y = 0; y < lev_fieldy; y++)
4317 for (x = 0; x < lev_fieldx; x++)
4319 int element = Feld[x][y];
4321 if (element == EL_LIGHT_SWITCH &&
4322 game.light_time_left > 0)
4324 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4325 DrawLevelField(x, y);
4327 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4328 game.light_time_left == 0)
4330 Feld[x][y] = EL_LIGHT_SWITCH;
4331 DrawLevelField(x, y);
4333 else if (element == EL_INVISIBLE_STEELWALL ||
4334 element == EL_INVISIBLE_WALL ||
4335 element == EL_INVISIBLE_SAND)
4337 if (game.light_time_left > 0)
4338 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4340 DrawLevelField(x, y);
4342 /* uncrumble neighbour fields, if needed */
4343 if (element == EL_INVISIBLE_SAND)
4344 DrawLevelFieldCrumbledSandNeighbours(x, y);
4346 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4347 element == EL_INVISIBLE_WALL_ACTIVE ||
4348 element == EL_INVISIBLE_SAND_ACTIVE)
4350 if (game.light_time_left == 0)
4351 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4353 DrawLevelField(x, y);
4355 /* re-crumble neighbour fields, if needed */
4356 if (element == EL_INVISIBLE_SAND)
4357 DrawLevelFieldCrumbledSandNeighbours(x, y);
4363 static void ToggleLightSwitch(int x, int y)
4365 int element = Feld[x][y];
4367 game.light_time_left =
4368 (element == EL_LIGHT_SWITCH ?
4369 level.time_light * FRAMES_PER_SECOND : 0);
4371 RedrawAllLightSwitchesAndInvisibleElements();
4374 static void ActivateTimegateSwitch(int x, int y)
4378 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4380 for (yy = 0; yy < lev_fieldy; yy++)
4382 for (xx = 0; xx < lev_fieldx; xx++)
4384 int element = Feld[xx][yy];
4386 if (element == EL_TIMEGATE_CLOSED ||
4387 element == EL_TIMEGATE_CLOSING)
4389 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4390 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4394 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4396 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4397 DrawLevelField(xx, yy);
4404 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4407 void Impact(int x, int y)
4409 boolean last_line = (y == lev_fieldy - 1);
4410 boolean object_hit = FALSE;
4411 boolean impact = (last_line || object_hit);
4412 int element = Feld[x][y];
4413 int smashed = EL_STEELWALL;
4416 printf("IMPACT!\n");
4419 if (!last_line) /* check if element below was hit */
4421 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4424 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4425 MovDir[x][y + 1] != MV_DOWN ||
4426 MovPos[x][y + 1] <= TILEY / 2));
4429 object_hit = !IS_FREE(x, y + 1);
4432 /* do not smash moving elements that left the smashed field in time */
4433 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4434 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4438 smashed = MovingOrBlocked2Element(x, y + 1);
4440 impact = (last_line || object_hit);
4443 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4445 SplashAcid(x, y + 1);
4449 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4450 /* only reset graphic animation if graphic really changes after impact */
4452 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4454 ResetGfxAnimation(x, y);
4455 DrawLevelField(x, y);
4458 if (impact && CAN_EXPLODE_IMPACT(element))
4463 else if (impact && element == EL_PEARL)
4465 ResetGfxAnimation(x, y);
4467 Feld[x][y] = EL_PEARL_BREAKING;
4468 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4471 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4473 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4478 if (impact && element == EL_AMOEBA_DROP)
4480 if (object_hit && IS_PLAYER(x, y + 1))
4481 KillHeroUnlessEnemyProtected(x, y + 1);
4482 else if (object_hit && smashed == EL_PENGUIN)
4486 Feld[x][y] = EL_AMOEBA_GROWING;
4487 Store[x][y] = EL_AMOEBA_WET;
4489 ResetRandomAnimationValue(x, y);
4494 if (object_hit) /* check which object was hit */
4496 if (CAN_PASS_MAGIC_WALL(element) &&
4497 (smashed == EL_MAGIC_WALL ||
4498 smashed == EL_BD_MAGIC_WALL))
4501 int activated_magic_wall =
4502 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4503 EL_BD_MAGIC_WALL_ACTIVE);
4505 /* activate magic wall / mill */
4506 for (yy = 0; yy < lev_fieldy; yy++)
4507 for (xx = 0; xx < lev_fieldx; xx++)
4508 if (Feld[xx][yy] == smashed)
4509 Feld[xx][yy] = activated_magic_wall;
4511 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4512 game.magic_wall_active = TRUE;
4514 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4515 SND_MAGIC_WALL_ACTIVATING :
4516 SND_BD_MAGIC_WALL_ACTIVATING));
4519 if (IS_PLAYER(x, y + 1))
4521 if (CAN_SMASH_PLAYER(element))
4523 KillHeroUnlessEnemyProtected(x, y + 1);
4527 else if (smashed == EL_PENGUIN)
4529 if (CAN_SMASH_PLAYER(element))
4535 else if (element == EL_BD_DIAMOND)
4537 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4543 else if (((element == EL_SP_INFOTRON ||
4544 element == EL_SP_ZONK) &&
4545 (smashed == EL_SP_SNIKSNAK ||
4546 smashed == EL_SP_ELECTRON ||
4547 smashed == EL_SP_DISK_ORANGE)) ||
4548 (element == EL_SP_INFOTRON &&
4549 smashed == EL_SP_DISK_YELLOW))
4555 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4561 else if (CAN_SMASH_EVERYTHING(element))
4563 if (IS_CLASSIC_ENEMY(smashed) ||
4564 CAN_EXPLODE_SMASHED(smashed))
4569 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4571 if (smashed == EL_LAMP ||
4572 smashed == EL_LAMP_ACTIVE)
4577 else if (smashed == EL_NUT)
4579 Feld[x][y + 1] = EL_NUT_BREAKING;
4580 PlayLevelSound(x, y, SND_NUT_BREAKING);
4581 RaiseScoreElement(EL_NUT);
4584 else if (smashed == EL_PEARL)
4586 ResetGfxAnimation(x, y);
4588 Feld[x][y + 1] = EL_PEARL_BREAKING;
4589 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4592 else if (smashed == EL_DIAMOND)
4594 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4595 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4598 else if (IS_BELT_SWITCH(smashed))
4600 ToggleBeltSwitch(x, y + 1);
4602 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4603 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4605 ToggleSwitchgateSwitch(x, y + 1);
4607 else if (smashed == EL_LIGHT_SWITCH ||
4608 smashed == EL_LIGHT_SWITCH_ACTIVE)
4610 ToggleLightSwitch(x, y + 1);
4615 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4618 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4621 /* !!! TEST ONLY !!! */
4622 CheckElementChangeBySide(x, y + 1, smashed, element,
4623 CE_SWITCHED, CH_SIDE_TOP);
4624 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4625 CE_SWITCH_OF_X, CH_SIDE_TOP);
4627 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4628 CE_SWITCH_OF_X, CH_SIDE_TOP);
4629 CheckElementChangeBySide(x, y + 1, smashed, element,
4630 CE_SWITCHED, CH_SIDE_TOP);
4636 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4641 /* play sound of magic wall / mill */
4643 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4644 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4646 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4647 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4648 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4649 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4654 /* play sound of object that hits the ground */
4655 if (last_line || object_hit)
4656 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4659 inline static void TurnRoundExt(int x, int y)
4671 { 0, 0 }, { 0, 0 }, { 0, 0 },
4676 int left, right, back;
4680 { MV_DOWN, MV_UP, MV_RIGHT },
4681 { MV_UP, MV_DOWN, MV_LEFT },
4683 { MV_LEFT, MV_RIGHT, MV_DOWN },
4687 { MV_RIGHT, MV_LEFT, MV_UP }
4690 int element = Feld[x][y];
4691 int move_pattern = element_info[element].move_pattern;
4693 int old_move_dir = MovDir[x][y];
4694 int left_dir = turn[old_move_dir].left;
4695 int right_dir = turn[old_move_dir].right;
4696 int back_dir = turn[old_move_dir].back;
4698 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4699 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4700 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4701 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4703 int left_x = x + left_dx, left_y = y + left_dy;
4704 int right_x = x + right_dx, right_y = y + right_dy;
4705 int move_x = x + move_dx, move_y = y + move_dy;
4709 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4711 TestIfBadThingTouchesOtherBadThing(x, y);
4713 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4714 MovDir[x][y] = right_dir;
4715 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4716 MovDir[x][y] = left_dir;
4718 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4720 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4724 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4725 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4727 TestIfBadThingTouchesOtherBadThing(x, y);
4729 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4730 MovDir[x][y] = left_dir;
4731 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4732 MovDir[x][y] = right_dir;
4734 if ((element == EL_SPACESHIP ||
4735 element == EL_SP_SNIKSNAK ||
4736 element == EL_SP_ELECTRON)
4737 && MovDir[x][y] != old_move_dir)
4739 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4743 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4745 TestIfBadThingTouchesOtherBadThing(x, y);
4747 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4748 MovDir[x][y] = left_dir;
4749 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4750 MovDir[x][y] = right_dir;
4752 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4754 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4757 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4759 TestIfBadThingTouchesOtherBadThing(x, y);
4761 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4762 MovDir[x][y] = left_dir;
4763 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4764 MovDir[x][y] = right_dir;
4766 if (MovDir[x][y] != old_move_dir)
4770 else if (element == EL_YAMYAM)
4772 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4773 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4775 if (can_turn_left && can_turn_right)
4776 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4777 else if (can_turn_left)
4778 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4779 else if (can_turn_right)
4780 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4782 MovDir[x][y] = back_dir;
4784 MovDelay[x][y] = 16 + 16 * RND(3);
4786 else if (element == EL_DARK_YAMYAM)
4788 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4790 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4793 if (can_turn_left && can_turn_right)
4794 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4795 else if (can_turn_left)
4796 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4797 else if (can_turn_right)
4798 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4800 MovDir[x][y] = back_dir;
4802 MovDelay[x][y] = 16 + 16 * RND(3);
4804 else if (element == EL_PACMAN)
4806 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4807 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4809 if (can_turn_left && can_turn_right)
4810 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4811 else if (can_turn_left)
4812 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4813 else if (can_turn_right)
4814 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4816 MovDir[x][y] = back_dir;
4818 MovDelay[x][y] = 6 + RND(40);
4820 else if (element == EL_PIG)
4822 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4823 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4824 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4825 boolean should_turn_left, should_turn_right, should_move_on;
4827 int rnd = RND(rnd_value);
4829 should_turn_left = (can_turn_left &&
4831 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4832 y + back_dy + left_dy)));
4833 should_turn_right = (can_turn_right &&
4835 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4836 y + back_dy + right_dy)));
4837 should_move_on = (can_move_on &&
4840 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4841 y + move_dy + left_dy) ||
4842 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4843 y + move_dy + right_dy)));
4845 if (should_turn_left || should_turn_right || should_move_on)
4847 if (should_turn_left && should_turn_right && should_move_on)
4848 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4849 rnd < 2 * rnd_value / 3 ? right_dir :
4851 else if (should_turn_left && should_turn_right)
4852 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4853 else if (should_turn_left && should_move_on)
4854 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4855 else if (should_turn_right && should_move_on)
4856 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4857 else if (should_turn_left)
4858 MovDir[x][y] = left_dir;
4859 else if (should_turn_right)
4860 MovDir[x][y] = right_dir;
4861 else if (should_move_on)
4862 MovDir[x][y] = old_move_dir;
4864 else if (can_move_on && rnd > rnd_value / 8)
4865 MovDir[x][y] = old_move_dir;
4866 else if (can_turn_left && can_turn_right)
4867 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4868 else if (can_turn_left && rnd > rnd_value / 8)
4869 MovDir[x][y] = left_dir;
4870 else if (can_turn_right && rnd > rnd_value/8)
4871 MovDir[x][y] = right_dir;
4873 MovDir[x][y] = back_dir;
4875 xx = x + move_xy[MovDir[x][y]].x;
4876 yy = y + move_xy[MovDir[x][y]].y;
4879 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4880 if (!IN_LEV_FIELD(xx, yy) ||
4881 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4882 MovDir[x][y] = old_move_dir;
4884 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4885 MovDir[x][y] = old_move_dir;
4890 else if (element == EL_DRAGON)
4892 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4893 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4894 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4896 int rnd = RND(rnd_value);
4899 if (FrameCounter < 1 && x == 0 && y == 29)
4900 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4903 if (can_move_on && rnd > rnd_value / 8)
4904 MovDir[x][y] = old_move_dir;
4905 else if (can_turn_left && can_turn_right)
4906 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4907 else if (can_turn_left && rnd > rnd_value / 8)
4908 MovDir[x][y] = left_dir;
4909 else if (can_turn_right && rnd > rnd_value / 8)
4910 MovDir[x][y] = right_dir;
4912 MovDir[x][y] = back_dir;
4914 xx = x + move_xy[MovDir[x][y]].x;
4915 yy = y + move_xy[MovDir[x][y]].y;
4918 if (FrameCounter < 1 && x == 0 && y == 29)
4919 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4920 xx, yy, Feld[xx][yy],
4925 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4926 MovDir[x][y] = old_move_dir;
4928 if (!IS_FREE(xx, yy))
4929 MovDir[x][y] = old_move_dir;
4933 if (FrameCounter < 1 && x == 0 && y == 29)
4934 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4939 else if (element == EL_MOLE)
4941 boolean can_move_on =
4942 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4943 IS_AMOEBOID(Feld[move_x][move_y]) ||
4944 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4947 boolean can_turn_left =
4948 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4949 IS_AMOEBOID(Feld[left_x][left_y])));
4951 boolean can_turn_right =
4952 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4953 IS_AMOEBOID(Feld[right_x][right_y])));
4955 if (can_turn_left && can_turn_right)
4956 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4957 else if (can_turn_left)
4958 MovDir[x][y] = left_dir;
4960 MovDir[x][y] = right_dir;
4963 if (MovDir[x][y] != old_move_dir)
4966 else if (element == EL_BALLOON)
4968 MovDir[x][y] = game.balloon_dir;
4971 else if (element == EL_SPRING)
4974 if (MovDir[x][y] & MV_HORIZONTAL &&
4975 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4976 MovDir[x][y] = MV_NO_MOVING;
4978 if (MovDir[x][y] & MV_HORIZONTAL &&
4979 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4980 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4981 MovDir[x][y] = MV_NO_MOVING;
4986 else if (element == EL_ROBOT ||
4987 element == EL_SATELLITE ||
4988 element == EL_PENGUIN)
4990 int attr_x = -1, attr_y = -1;
5001 for (i = 0; i < MAX_PLAYERS; i++)
5003 struct PlayerInfo *player = &stored_player[i];
5004 int jx = player->jx, jy = player->jy;
5006 if (!player->active)
5010 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5019 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5020 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5021 game.engine_version < VERSION_IDENT(3,1,0,0)))
5023 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5030 if (element == EL_PENGUIN)
5033 static int xy[4][2] =
5041 for (i = 0; i < NUM_DIRECTIONS; i++)
5043 int ex = x + xy[i][0];
5044 int ey = y + xy[i][1];
5046 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5055 MovDir[x][y] = MV_NO_MOVING;
5057 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5058 else if (attr_x > x)
5059 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5061 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5062 else if (attr_y > y)
5063 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5065 if (element == EL_ROBOT)
5069 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5070 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5071 Moving2Blocked(x, y, &newx, &newy);
5073 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5074 MovDelay[x][y] = 8 + 8 * !RND(3);
5076 MovDelay[x][y] = 16;
5078 else if (element == EL_PENGUIN)
5084 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5086 boolean first_horiz = RND(2);
5087 int new_move_dir = MovDir[x][y];
5090 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5091 Moving2Blocked(x, y, &newx, &newy);
5093 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5097 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5098 Moving2Blocked(x, y, &newx, &newy);
5100 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5103 MovDir[x][y] = old_move_dir;
5107 else /* (element == EL_SATELLITE) */
5113 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5115 boolean first_horiz = RND(2);
5116 int new_move_dir = MovDir[x][y];
5119 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5120 Moving2Blocked(x, y, &newx, &newy);
5122 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5126 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5127 Moving2Blocked(x, y, &newx, &newy);
5129 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5132 MovDir[x][y] = old_move_dir;
5137 else if (move_pattern == MV_TURNING_LEFT ||
5138 move_pattern == MV_TURNING_RIGHT ||
5139 move_pattern == MV_TURNING_LEFT_RIGHT ||
5140 move_pattern == MV_TURNING_RIGHT_LEFT ||
5141 move_pattern == MV_TURNING_RANDOM ||
5142 move_pattern == MV_ALL_DIRECTIONS)
5144 boolean can_turn_left =
5145 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5146 boolean can_turn_right =
5147 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5149 #if USE_CAN_MOVE_NOT_MOVING
5150 if (element_info[element].move_stepsize == 0) /* not moving */
5154 if (move_pattern == MV_TURNING_LEFT)
5155 MovDir[x][y] = left_dir;
5156 else if (move_pattern == MV_TURNING_RIGHT)
5157 MovDir[x][y] = right_dir;
5158 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5159 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5160 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5161 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5162 else if (move_pattern == MV_TURNING_RANDOM)
5163 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5164 can_turn_right && !can_turn_left ? right_dir :
5165 RND(2) ? left_dir : right_dir);
5166 else if (can_turn_left && can_turn_right)
5167 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5168 else if (can_turn_left)
5169 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5170 else if (can_turn_right)
5171 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5173 MovDir[x][y] = back_dir;
5175 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5177 else if (move_pattern == MV_HORIZONTAL ||
5178 move_pattern == MV_VERTICAL)
5180 if (move_pattern & old_move_dir)
5181 MovDir[x][y] = back_dir;
5182 else if (move_pattern == MV_HORIZONTAL)
5183 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5184 else if (move_pattern == MV_VERTICAL)
5185 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5187 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5189 else if (move_pattern & MV_ANY_DIRECTION)
5191 MovDir[x][y] = move_pattern;
5192 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5194 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5196 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5197 MovDir[x][y] = left_dir;
5198 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5199 MovDir[x][y] = right_dir;
5201 if (MovDir[x][y] != old_move_dir)
5202 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5204 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5206 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5207 MovDir[x][y] = right_dir;
5208 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5209 MovDir[x][y] = left_dir;
5211 if (MovDir[x][y] != old_move_dir)
5212 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5214 else if (move_pattern == MV_TOWARDS_PLAYER ||
5215 move_pattern == MV_AWAY_FROM_PLAYER)
5217 int attr_x = -1, attr_y = -1;
5219 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5230 for (i = 0; i < MAX_PLAYERS; i++)
5232 struct PlayerInfo *player = &stored_player[i];
5233 int jx = player->jx, jy = player->jy;
5235 if (!player->active)
5239 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5247 MovDir[x][y] = MV_NO_MOVING;
5249 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5250 else if (attr_x > x)
5251 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5253 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5254 else if (attr_y > y)
5255 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5257 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5259 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5261 boolean first_horiz = RND(2);
5262 int new_move_dir = MovDir[x][y];
5264 #if USE_CAN_MOVE_NOT_MOVING
5265 if (element_info[element].move_stepsize == 0) /* not moving */
5267 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5268 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5275 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5276 Moving2Blocked(x, y, &newx, &newy);
5278 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5282 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5283 Moving2Blocked(x, y, &newx, &newy);
5285 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5288 MovDir[x][y] = old_move_dir;
5291 else if (move_pattern == MV_WHEN_PUSHED ||
5292 move_pattern == MV_WHEN_DROPPED)
5294 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5295 MovDir[x][y] = MV_NO_MOVING;
5299 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5301 static int test_xy[7][2] =
5311 static int test_dir[7] =
5321 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5322 int move_preference = -1000000; /* start with very low preference */
5323 int new_move_dir = MV_NO_MOVING;
5324 int start_test = RND(4);
5327 for (i = 0; i < NUM_DIRECTIONS; i++)
5329 int move_dir = test_dir[start_test + i];
5330 int move_dir_preference;
5332 xx = x + test_xy[start_test + i][0];
5333 yy = y + test_xy[start_test + i][1];
5335 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5336 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5338 new_move_dir = move_dir;
5343 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5346 move_dir_preference = -1 * RunnerVisit[xx][yy];
5347 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5348 move_dir_preference = PlayerVisit[xx][yy];
5350 if (move_dir_preference > move_preference)
5352 /* prefer field that has not been visited for the longest time */
5353 move_preference = move_dir_preference;
5354 new_move_dir = move_dir;
5356 else if (move_dir_preference == move_preference &&
5357 move_dir == old_move_dir)
5359 /* prefer last direction when all directions are preferred equally */
5360 move_preference = move_dir_preference;
5361 new_move_dir = move_dir;
5365 MovDir[x][y] = new_move_dir;
5366 if (old_move_dir != new_move_dir)
5369 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5377 static void TurnRound(int x, int y)
5379 int direction = MovDir[x][y];
5382 GfxDir[x][y] = MovDir[x][y];
5388 GfxDir[x][y] = MovDir[x][y];
5391 if (direction != MovDir[x][y])
5396 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5399 GfxAction[x][y] = ACTION_WAITING;
5403 static boolean JustBeingPushed(int x, int y)
5407 for (i = 0; i < MAX_PLAYERS; i++)
5409 struct PlayerInfo *player = &stored_player[i];
5411 if (player->active && player->is_pushing && player->MovPos)
5413 int next_jx = player->jx + (player->jx - player->last_jx);
5414 int next_jy = player->jy + (player->jy - player->last_jy);
5416 if (x == next_jx && y == next_jy)
5424 void StartMoving(int x, int y)
5427 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5429 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5430 int element = Feld[x][y];
5436 if (MovDelay[x][y] == 0)
5437 GfxAction[x][y] = ACTION_DEFAULT;
5439 /* !!! this should be handled more generic (not only for mole) !!! */
5440 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5441 GfxAction[x][y] = ACTION_DEFAULT;
5444 if (CAN_FALL(element) && y < lev_fieldy - 1)
5446 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5447 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5448 if (JustBeingPushed(x, y))
5451 if (element == EL_QUICKSAND_FULL)
5453 if (IS_FREE(x, y + 1))
5455 InitMovingField(x, y, MV_DOWN);
5456 started_moving = TRUE;
5458 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5459 Store[x][y] = EL_ROCK;
5461 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5463 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5466 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5468 if (!MovDelay[x][y])
5469 MovDelay[x][y] = TILEY + 1;
5478 Feld[x][y] = EL_QUICKSAND_EMPTY;
5479 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5480 Store[x][y + 1] = Store[x][y];
5483 PlayLevelSoundAction(x, y, ACTION_FILLING);
5485 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5489 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5490 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5492 InitMovingField(x, y, MV_DOWN);
5493 started_moving = TRUE;
5495 Feld[x][y] = EL_QUICKSAND_FILLING;
5496 Store[x][y] = element;
5498 PlayLevelSoundAction(x, y, ACTION_FILLING);
5500 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5503 else if (element == EL_MAGIC_WALL_FULL)
5505 if (IS_FREE(x, y + 1))
5507 InitMovingField(x, y, MV_DOWN);
5508 started_moving = TRUE;
5510 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5511 Store[x][y] = EL_CHANGED(Store[x][y]);
5513 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5515 if (!MovDelay[x][y])
5516 MovDelay[x][y] = TILEY/4 + 1;
5525 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5526 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5527 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5531 else if (element == EL_BD_MAGIC_WALL_FULL)
5533 if (IS_FREE(x, y + 1))
5535 InitMovingField(x, y, MV_DOWN);
5536 started_moving = TRUE;
5538 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5539 Store[x][y] = EL_CHANGED2(Store[x][y]);
5541 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5543 if (!MovDelay[x][y])
5544 MovDelay[x][y] = TILEY/4 + 1;
5553 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5554 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5555 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5559 else if (CAN_PASS_MAGIC_WALL(element) &&
5560 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5561 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5563 InitMovingField(x, y, MV_DOWN);
5564 started_moving = TRUE;
5567 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5568 EL_BD_MAGIC_WALL_FILLING);
5569 Store[x][y] = element;
5572 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5574 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5577 SplashAcid(x, y + 1);
5579 InitMovingField(x, y, MV_DOWN);
5580 started_moving = TRUE;
5582 Store[x][y] = EL_ACID;
5584 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5585 GfxAction[x][y + 1] = ACTION_ACTIVE;
5589 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5590 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5592 #if USE_IMPACT_BUGFIX
5593 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5594 CAN_FALL(element) && WasJustFalling[x][y] &&
5595 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5597 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5598 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5599 (Feld[x][y + 1] == EL_BLOCKED)))
5601 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5602 CAN_SMASH(element) && WasJustFalling[x][y] &&
5603 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5605 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5606 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5607 (Feld[x][y + 1] == EL_BLOCKED)))
5612 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5613 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5614 WasJustMoving[x][y] && !Pushed[x][y + 1])
5616 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5617 WasJustMoving[x][y])
5622 /* this is needed for a special case not covered by calling "Impact()"
5623 from "ContinueMoving()": if an element moves to a tile directly below
5624 another element which was just falling on that tile (which was empty
5625 in the previous frame), the falling element above would just stop
5626 instead of smashing the element below (in previous version, the above
5627 element was just checked for "moving" instead of "falling", resulting
5628 in incorrect smashes caused by horizontal movement of the above
5629 element; also, the case of the player being the element to smash was
5630 simply not covered here... :-/ ) */
5633 WasJustMoving[x][y] = 0;
5634 WasJustFalling[x][y] = 0;
5637 CheckCollision[x][y] = 0;
5640 if (IS_PLAYER(x, y + 1))
5641 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5646 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5648 if (MovDir[x][y] == MV_NO_MOVING)
5650 InitMovingField(x, y, MV_DOWN);
5651 started_moving = TRUE;
5654 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5656 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5657 MovDir[x][y] = MV_DOWN;
5659 InitMovingField(x, y, MV_DOWN);
5660 started_moving = TRUE;
5662 else if (element == EL_AMOEBA_DROP)
5664 Feld[x][y] = EL_AMOEBA_GROWING;
5665 Store[x][y] = EL_AMOEBA_WET;
5667 /* Store[x][y + 1] must be zero, because:
5668 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5671 #if OLD_GAME_BEHAVIOUR
5672 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5674 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5675 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5676 element != EL_DX_SUPABOMB)
5679 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5680 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5681 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5682 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5685 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5686 (IS_FREE(x - 1, y + 1) ||
5687 Feld[x - 1][y + 1] == EL_ACID));
5688 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5689 (IS_FREE(x + 1, y + 1) ||
5690 Feld[x + 1][y + 1] == EL_ACID));
5691 boolean can_fall_any = (can_fall_left || can_fall_right);
5692 boolean can_fall_both = (can_fall_left && can_fall_right);
5694 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5696 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5698 if (slippery_type == SLIPPERY_ONLY_LEFT)
5699 can_fall_right = FALSE;
5700 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5701 can_fall_left = FALSE;
5702 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5703 can_fall_right = FALSE;
5704 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5705 can_fall_left = FALSE;
5707 can_fall_any = (can_fall_left || can_fall_right);
5708 can_fall_both = (can_fall_left && can_fall_right);
5711 #if USE_NEW_SP_SLIPPERY
5712 /* !!! better use the same properties as for custom elements here !!! */
5713 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5714 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5716 can_fall_right = FALSE; /* slip down on left side */
5717 can_fall_both = FALSE;
5724 if (game.emulation == EMU_BOULDERDASH ||
5725 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5726 can_fall_right = FALSE; /* slip down on left side */
5728 can_fall_left = !(can_fall_right = RND(2));
5730 can_fall_both = FALSE;
5737 if (can_fall_both &&
5738 (game.emulation != EMU_BOULDERDASH &&
5739 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5740 can_fall_left = !(can_fall_right = RND(2));
5743 /* if not determined otherwise, prefer left side for slipping down */
5744 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5745 started_moving = TRUE;
5749 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5751 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5754 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5755 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5756 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5757 int belt_dir = game.belt_dir[belt_nr];
5759 if ((belt_dir == MV_LEFT && left_is_free) ||
5760 (belt_dir == MV_RIGHT && right_is_free))
5763 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5766 InitMovingField(x, y, belt_dir);
5767 started_moving = TRUE;
5770 Pushed[x][y] = TRUE;
5771 Pushed[nextx][y] = TRUE;
5774 GfxAction[x][y] = ACTION_DEFAULT;
5778 MovDir[x][y] = 0; /* if element was moving, stop it */
5783 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5785 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5787 if (CAN_MOVE(element) && !started_moving)
5790 int move_pattern = element_info[element].move_pattern;
5795 if (MovDir[x][y] == MV_NO_MOVING)
5797 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5798 x, y, element, element_info[element].token_name);
5799 printf("StartMoving(): This should never happen!\n");
5804 Moving2Blocked(x, y, &newx, &newy);
5807 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5810 if ((element == EL_SATELLITE ||
5811 element == EL_BALLOON ||
5812 element == EL_SPRING)
5813 && JustBeingPushed(x, y))
5820 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5821 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5823 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5824 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5825 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5829 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5830 element, element_info[element].token_name,
5831 WasJustMoving[x][y],
5832 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5833 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5834 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5835 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5839 WasJustMoving[x][y] = 0;
5842 CheckCollision[x][y] = 0;
5844 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5847 if (Feld[x][y] != element) /* element has changed */
5849 element = Feld[x][y];
5850 move_pattern = element_info[element].move_pattern;
5852 if (!CAN_MOVE(element))
5856 if (Feld[x][y] != element) /* element has changed */
5864 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5865 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5867 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5869 Moving2Blocked(x, y, &newx, &newy);
5870 if (Feld[newx][newy] == EL_BLOCKED)
5871 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5877 if (FrameCounter < 1 && x == 0 && y == 29)
5878 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5881 if (!MovDelay[x][y]) /* start new movement phase */
5883 /* all objects that can change their move direction after each step
5884 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5886 if (element != EL_YAMYAM &&
5887 element != EL_DARK_YAMYAM &&
5888 element != EL_PACMAN &&
5889 !(move_pattern & MV_ANY_DIRECTION) &&
5890 move_pattern != MV_TURNING_LEFT &&
5891 move_pattern != MV_TURNING_RIGHT &&
5892 move_pattern != MV_TURNING_LEFT_RIGHT &&
5893 move_pattern != MV_TURNING_RIGHT_LEFT &&
5894 move_pattern != MV_TURNING_RANDOM)
5899 if (FrameCounter < 1 && x == 0 && y == 29)
5900 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5903 if (MovDelay[x][y] && (element == EL_BUG ||
5904 element == EL_SPACESHIP ||
5905 element == EL_SP_SNIKSNAK ||
5906 element == EL_SP_ELECTRON ||
5907 element == EL_MOLE))
5908 DrawLevelField(x, y);
5912 if (MovDelay[x][y]) /* wait some time before next movement */
5917 if (element == EL_YAMYAM)
5920 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5921 DrawLevelElementAnimation(x, y, element);
5925 if (MovDelay[x][y]) /* element still has to wait some time */
5928 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5929 ResetGfxAnimation(x, y);
5933 if (GfxAction[x][y] != ACTION_WAITING)
5934 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5936 GfxAction[x][y] = ACTION_WAITING;
5940 if (element == EL_ROBOT ||
5942 element == EL_PACMAN ||
5944 element == EL_YAMYAM ||
5945 element == EL_DARK_YAMYAM)
5948 DrawLevelElementAnimation(x, y, element);
5950 DrawLevelElementAnimationIfNeeded(x, y, element);
5952 PlayLevelSoundAction(x, y, ACTION_WAITING);
5954 else if (element == EL_SP_ELECTRON)
5955 DrawLevelElementAnimationIfNeeded(x, y, element);
5956 else if (element == EL_DRAGON)
5959 int dir = MovDir[x][y];
5960 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5961 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5962 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5963 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5964 dir == MV_UP ? IMG_FLAMES_1_UP :
5965 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5966 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5969 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5972 GfxAction[x][y] = ACTION_ATTACKING;
5974 if (IS_PLAYER(x, y))
5975 DrawPlayerField(x, y);
5977 DrawLevelField(x, y);
5979 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5981 for (i = 1; i <= 3; i++)
5983 int xx = x + i * dx;
5984 int yy = y + i * dy;
5985 int sx = SCREENX(xx);
5986 int sy = SCREENY(yy);
5987 int flame_graphic = graphic + (i - 1);
5989 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5994 int flamed = MovingOrBlocked2Element(xx, yy);
5998 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6000 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6001 RemoveMovingField(xx, yy);
6003 RemoveField(xx, yy);
6005 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6008 RemoveMovingField(xx, yy);
6012 if (ChangeDelay[xx][yy])
6013 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6014 Feld[xx][yy] == EL_BLOCKED));
6018 ChangeDelay[xx][yy] = 0;
6020 Feld[xx][yy] = EL_FLAMES;
6021 if (IN_SCR_FIELD(sx, sy))
6023 DrawLevelFieldCrumbledSand(xx, yy);
6024 DrawGraphic(sx, sy, flame_graphic, frame);
6029 if (Feld[xx][yy] == EL_FLAMES)
6030 Feld[xx][yy] = EL_EMPTY;
6031 DrawLevelField(xx, yy);
6036 if (MovDelay[x][y]) /* element still has to wait some time */
6038 PlayLevelSoundAction(x, y, ACTION_WAITING);
6044 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6045 for all other elements GfxAction will be set by InitMovingField() */
6046 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6047 GfxAction[x][y] = ACTION_MOVING;
6051 /* now make next step */
6053 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6055 if (DONT_COLLIDE_WITH(element) &&
6056 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6057 !PLAYER_ENEMY_PROTECTED(newx, newy))
6060 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6064 /* player killed by element which is deadly when colliding with */
6066 KillHero(PLAYERINFO(newx, newy));
6073 else if (CAN_MOVE_INTO_ACID(element) &&
6074 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6075 (MovDir[x][y] == MV_DOWN ||
6076 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6078 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6079 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6083 else if ((element == EL_PENGUIN ||
6084 element == EL_ROBOT ||
6085 element == EL_SATELLITE ||
6086 element == EL_BALLOON ||
6087 IS_CUSTOM_ELEMENT(element)) &&
6088 IN_LEV_FIELD(newx, newy) &&
6089 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6092 SplashAcid(newx, newy);
6093 Store[x][y] = EL_ACID;
6095 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6097 if (Feld[newx][newy] == EL_EXIT_OPEN)
6101 DrawLevelField(x, y);
6103 Feld[x][y] = EL_EMPTY;
6104 DrawLevelField(x, y);
6107 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6108 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6109 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6111 local_player->friends_still_needed--;
6112 if (!local_player->friends_still_needed &&
6113 !local_player->GameOver && AllPlayersGone)
6114 local_player->LevelSolved = local_player->GameOver = TRUE;
6118 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6120 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6121 DrawLevelField(newx, newy);
6123 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6125 else if (!IS_FREE(newx, newy))
6127 GfxAction[x][y] = ACTION_WAITING;
6129 if (IS_PLAYER(x, y))
6130 DrawPlayerField(x, y);
6132 DrawLevelField(x, y);
6137 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6139 if (IS_FOOD_PIG(Feld[newx][newy]))
6141 if (IS_MOVING(newx, newy))
6142 RemoveMovingField(newx, newy);
6145 Feld[newx][newy] = EL_EMPTY;
6146 DrawLevelField(newx, newy);
6149 PlayLevelSound(x, y, SND_PIG_DIGGING);
6151 else if (!IS_FREE(newx, newy))
6153 if (IS_PLAYER(x, y))
6154 DrawPlayerField(x, y);
6156 DrawLevelField(x, y);
6165 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6168 else if (IS_CUSTOM_ELEMENT(element) &&
6169 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6173 !IS_FREE(newx, newy)
6178 int new_element = Feld[newx][newy];
6181 printf("::: '%s' digs '%s' [%d]\n",
6182 element_info[element].token_name,
6183 element_info[Feld[newx][newy]].token_name,
6184 StorePlayer[newx][newy]);
6187 if (!IS_FREE(newx, newy))
6189 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6190 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6193 /* no element can dig solid indestructible elements */
6194 if (IS_INDESTRUCTIBLE(new_element) &&
6195 !IS_DIGGABLE(new_element) &&
6196 !IS_COLLECTIBLE(new_element))
6199 if (AmoebaNr[newx][newy] &&
6200 (new_element == EL_AMOEBA_FULL ||
6201 new_element == EL_BD_AMOEBA ||
6202 new_element == EL_AMOEBA_GROWING))
6204 AmoebaCnt[AmoebaNr[newx][newy]]--;
6205 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6208 if (IS_MOVING(newx, newy))
6209 RemoveMovingField(newx, newy);
6212 RemoveField(newx, newy);
6213 DrawLevelField(newx, newy);
6216 /* if digged element was about to explode, prevent the explosion */
6217 ExplodeField[newx][newy] = EX_TYPE_NONE;
6219 PlayLevelSoundAction(x, y, action);
6224 Store[newx][newy] = EL_EMPTY;
6225 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6227 #if USE_CHANGE_TO_TRIGGERED
6228 int move_leave_element = element_info[element].move_leave_element;
6230 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6231 new_element : move_leave_element);
6233 Store[newx][newy] = element_info[element].move_leave_element;
6237 Store[newx][newy] = EL_EMPTY;
6238 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6239 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6240 Store[newx][newy] = element_info[element].move_leave_element;
6243 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6244 element_info[element].can_leave_element = TRUE;
6247 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6249 RunnerVisit[x][y] = FrameCounter;
6250 PlayerVisit[x][y] /= 8; /* expire player visit path */
6256 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6258 if (!IS_FREE(newx, newy))
6260 if (IS_PLAYER(x, y))
6261 DrawPlayerField(x, y);
6263 DrawLevelField(x, y);
6269 boolean wanna_flame = !RND(10);
6270 int dx = newx - x, dy = newy - y;
6271 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6272 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6273 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6274 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6275 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6276 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6279 IS_CLASSIC_ENEMY(element1) ||
6280 IS_CLASSIC_ENEMY(element2)) &&
6281 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6282 element1 != EL_FLAMES && element2 != EL_FLAMES)
6285 ResetGfxAnimation(x, y);
6286 GfxAction[x][y] = ACTION_ATTACKING;
6289 if (IS_PLAYER(x, y))
6290 DrawPlayerField(x, y);
6292 DrawLevelField(x, y);
6294 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6296 MovDelay[x][y] = 50;
6300 RemoveField(newx, newy);
6302 Feld[newx][newy] = EL_FLAMES;
6303 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6306 RemoveField(newx1, newy1);
6308 Feld[newx1][newy1] = EL_FLAMES;
6310 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6313 RemoveField(newx2, newy2);
6315 Feld[newx2][newy2] = EL_FLAMES;
6322 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6323 Feld[newx][newy] == EL_DIAMOND)
6325 if (IS_MOVING(newx, newy))
6326 RemoveMovingField(newx, newy);
6329 Feld[newx][newy] = EL_EMPTY;
6330 DrawLevelField(newx, newy);
6333 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6335 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6336 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6338 if (AmoebaNr[newx][newy])
6340 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6341 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6342 Feld[newx][newy] == EL_BD_AMOEBA)
6343 AmoebaCnt[AmoebaNr[newx][newy]]--;
6348 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6350 if (IS_MOVING(newx, newy))
6353 RemoveMovingField(newx, newy);
6357 Feld[newx][newy] = EL_EMPTY;
6358 DrawLevelField(newx, newy);
6361 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6363 else if ((element == EL_PACMAN || element == EL_MOLE)
6364 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6366 if (AmoebaNr[newx][newy])
6368 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6369 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6370 Feld[newx][newy] == EL_BD_AMOEBA)
6371 AmoebaCnt[AmoebaNr[newx][newy]]--;
6374 if (element == EL_MOLE)
6376 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6377 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6379 ResetGfxAnimation(x, y);
6380 GfxAction[x][y] = ACTION_DIGGING;
6381 DrawLevelField(x, y);
6383 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6385 return; /* wait for shrinking amoeba */
6387 else /* element == EL_PACMAN */
6389 Feld[newx][newy] = EL_EMPTY;
6390 DrawLevelField(newx, newy);
6391 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6394 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6395 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6396 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6398 /* wait for shrinking amoeba to completely disappear */
6401 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6403 /* object was running against a wall */
6408 if (move_pattern & MV_ANY_DIRECTION &&
6409 move_pattern == MovDir[x][y])
6411 int blocking_element =
6412 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6415 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6416 element_info[element].token_name,
6417 element_info[blocking_element].token_name,
6421 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6424 element = Feld[x][y]; /* element might have changed */
6429 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6430 DrawLevelElementAnimation(x, y, element);
6432 if (element == EL_BUG ||
6433 element == EL_SPACESHIP ||
6434 element == EL_SP_SNIKSNAK)
6435 DrawLevelField(x, y);
6436 else if (element == EL_MOLE)
6437 DrawLevelField(x, y);
6438 else if (element == EL_BD_BUTTERFLY ||
6439 element == EL_BD_FIREFLY)
6440 DrawLevelElementAnimationIfNeeded(x, y, element);
6441 else if (element == EL_SATELLITE)
6442 DrawLevelElementAnimationIfNeeded(x, y, element);
6443 else if (element == EL_SP_ELECTRON)
6444 DrawLevelElementAnimationIfNeeded(x, y, element);
6447 if (DONT_TOUCH(element))
6448 TestIfBadThingTouchesHero(x, y);
6451 PlayLevelSoundAction(x, y, ACTION_WAITING);
6457 InitMovingField(x, y, MovDir[x][y]);
6459 PlayLevelSoundAction(x, y, ACTION_MOVING);
6463 ContinueMoving(x, y);
6470 void ContinueMoving(int x, int y)
6472 int element = Feld[x][y];
6473 int stored = Store[x][y];
6474 struct ElementInfo *ei = &element_info[element];
6475 int direction = MovDir[x][y];
6476 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6477 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6478 int newx = x + dx, newy = y + dy;
6480 int nextx = newx + dx, nexty = newy + dy;
6483 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6484 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6486 boolean pushed_by_player = Pushed[x][y];
6488 boolean last_line = (newy == lev_fieldy - 1);
6490 MovPos[x][y] += getElementMoveStepsize(x, y);
6493 if (pushed_by_player && IS_PLAYER(x, y))
6495 /* special case: moving object pushed by player */
6496 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6499 if (pushed_by_player) /* special case: moving object pushed by player */
6500 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6503 if (ABS(MovPos[x][y]) < TILEX)
6505 DrawLevelField(x, y);
6507 return; /* element is still moving */
6510 /* element reached destination field */
6512 Feld[x][y] = EL_EMPTY;
6513 Feld[newx][newy] = element;
6514 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6517 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6519 element = Feld[newx][newy] = EL_ACID;
6522 else if (element == EL_MOLE)
6524 Feld[x][y] = EL_SAND;
6526 DrawLevelFieldCrumbledSandNeighbours(x, y);
6528 else if (element == EL_QUICKSAND_FILLING)
6530 element = Feld[newx][newy] = get_next_element(element);
6531 Store[newx][newy] = Store[x][y];
6533 else if (element == EL_QUICKSAND_EMPTYING)
6535 Feld[x][y] = get_next_element(element);
6536 element = Feld[newx][newy] = Store[x][y];
6538 else if (element == EL_MAGIC_WALL_FILLING)
6540 element = Feld[newx][newy] = get_next_element(element);
6541 if (!game.magic_wall_active)
6542 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6543 Store[newx][newy] = Store[x][y];
6545 else if (element == EL_MAGIC_WALL_EMPTYING)
6547 Feld[x][y] = get_next_element(element);
6548 if (!game.magic_wall_active)
6549 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6550 element = Feld[newx][newy] = Store[x][y];
6552 else if (element == EL_BD_MAGIC_WALL_FILLING)
6554 element = Feld[newx][newy] = get_next_element(element);
6555 if (!game.magic_wall_active)
6556 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6557 Store[newx][newy] = Store[x][y];
6559 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6561 Feld[x][y] = get_next_element(element);
6562 if (!game.magic_wall_active)
6563 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6564 element = Feld[newx][newy] = Store[x][y];
6566 else if (element == EL_AMOEBA_DROPPING)
6568 Feld[x][y] = get_next_element(element);
6569 element = Feld[newx][newy] = Store[x][y];
6571 else if (element == EL_SOKOBAN_OBJECT)
6574 Feld[x][y] = Back[x][y];
6576 if (Back[newx][newy])
6577 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6579 Back[x][y] = Back[newx][newy] = 0;
6582 else if (Store[x][y] == EL_ACID)
6584 element = Feld[newx][newy] = EL_ACID;
6588 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6589 ei->move_leave_element != EL_EMPTY &&
6590 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6591 Store[x][y] != EL_EMPTY))
6593 /* some elements can leave other elements behind after moving */
6595 Feld[x][y] = ei->move_leave_element;
6596 InitField(x, y, FALSE);
6598 if (GFX_CRUMBLED(Feld[x][y]))
6599 DrawLevelFieldCrumbledSandNeighbours(x, y);
6603 Store[x][y] = EL_EMPTY;
6607 MovDelay[newx][newy] = 0;
6609 if (CAN_CHANGE(element))
6611 /* copy element change control values to new field */
6612 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6613 ChangePage[newx][newy] = ChangePage[x][y];
6614 Changed[newx][newy] = Changed[x][y];
6615 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6618 ChangeDelay[x][y] = 0;
6619 ChangePage[x][y] = -1;
6620 Changed[x][y] = FALSE;
6621 ChangeEvent[x][y] = -1;
6623 /* copy animation control values to new field */
6624 GfxFrame[newx][newy] = GfxFrame[x][y];
6625 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6626 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6627 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6629 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6632 /* do this after checking for left-behind element */
6633 ResetGfxAnimation(x, y); /* reset animation values for old field */
6637 /* some elements can leave other elements behind after moving */
6639 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6640 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6641 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6643 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6644 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6648 int move_leave_element = ei->move_leave_element;
6650 #if USE_CHANGE_TO_TRIGGERED
6651 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6652 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6653 move_leave_element = stored;
6656 Feld[x][y] = move_leave_element;
6658 #if USE_PREVIOUS_MOVE_DIR
6659 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6660 MovDir[x][y] = direction;
6663 InitField(x, y, FALSE);
6665 if (GFX_CRUMBLED(Feld[x][y]))
6666 DrawLevelFieldCrumbledSandNeighbours(x, y);
6668 if (ELEM_IS_PLAYER(move_leave_element))
6669 RelocatePlayer(x, y, move_leave_element);
6674 /* some elements can leave other elements behind after moving */
6675 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6676 ei->move_leave_element != EL_EMPTY &&
6677 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6678 ei->can_leave_element_last))
6680 Feld[x][y] = ei->move_leave_element;
6681 InitField(x, y, FALSE);
6683 if (GFX_CRUMBLED(Feld[x][y]))
6684 DrawLevelFieldCrumbledSandNeighbours(x, y);
6687 ei->can_leave_element_last = ei->can_leave_element;
6688 ei->can_leave_element = FALSE;
6692 /* do this after checking for left-behind element */
6693 ResetGfxAnimation(x, y); /* reset animation values for old field */
6697 /* 2.1.1 (does not work correctly for spring) */
6698 if (!CAN_MOVE(element))
6699 MovDir[newx][newy] = 0;
6703 /* (does not work for falling objects that slide horizontally) */
6704 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6705 MovDir[newx][newy] = 0;
6708 if (!CAN_MOVE(element) ||
6709 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6710 MovDir[newx][newy] = 0;
6714 if (!CAN_MOVE(element) ||
6715 (CAN_FALL(element) && direction == MV_DOWN))
6716 GfxDir[x][y] = MovDir[newx][newy] = 0;
6718 if (!CAN_MOVE(element) ||
6719 (CAN_FALL(element) && direction == MV_DOWN &&
6720 (element == EL_SPRING ||
6721 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6722 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6723 GfxDir[x][y] = MovDir[newx][newy] = 0;
6729 DrawLevelField(x, y);
6730 DrawLevelField(newx, newy);
6732 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6734 /* prevent pushed element from moving on in pushed direction */
6735 if (pushed_by_player && CAN_MOVE(element) &&
6736 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6737 !(element_info[element].move_pattern & direction))
6738 TurnRound(newx, newy);
6741 /* prevent elements on conveyor belt from moving on in last direction */
6742 if (pushed_by_conveyor && CAN_FALL(element) &&
6743 direction & MV_HORIZONTAL)
6746 if (CAN_MOVE(element))
6747 InitMovDir(newx, newy);
6749 MovDir[newx][newy] = 0;
6751 MovDir[newx][newy] = 0;
6756 if (!pushed_by_player)
6758 int nextx = newx + dx, nexty = newy + dy;
6759 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6761 WasJustMoving[newx][newy] = 3;
6763 if (CAN_FALL(element) && direction == MV_DOWN)
6764 WasJustFalling[newx][newy] = 3;
6766 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6767 CheckCollision[newx][newy] = 2;
6770 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6772 TestIfBadThingTouchesHero(newx, newy);
6773 TestIfBadThingTouchesFriend(newx, newy);
6775 if (!IS_CUSTOM_ELEMENT(element))
6776 TestIfBadThingTouchesOtherBadThing(newx, newy);
6778 else if (element == EL_PENGUIN)
6779 TestIfFriendTouchesBadThing(newx, newy);
6781 #if USE_NEW_MOVE_STYLE
6783 if (CAN_FALL(element) && direction == MV_DOWN &&
6784 !last_line && IS_PLAYER(x, newy + 1))
6785 printf("::: we would now kill the player [%d]\n", FrameCounter);
6788 /* give the player one last chance (one more frame) to move away */
6789 if (CAN_FALL(element) && direction == MV_DOWN &&
6790 (last_line || (!IS_FREE(x, newy + 1) &&
6791 (!IS_PLAYER(x, newy + 1) ||
6792 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6795 if (CAN_FALL(element) && direction == MV_DOWN &&
6796 (last_line || !IS_FREE(x, newy + 1)))
6804 if (pushed_by_player && !game.use_change_when_pushing_bug)
6806 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6809 if (pushed_by_player)
6814 int dig_side = MV_DIR_OPPOSITE(direction);
6816 static int trigger_sides[4] =
6818 CH_SIDE_RIGHT, /* moving left */
6819 CH_SIDE_LEFT, /* moving right */
6820 CH_SIDE_BOTTOM, /* moving up */
6821 CH_SIDE_TOP, /* moving down */
6823 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6825 struct PlayerInfo *player = PLAYERINFO(x, y);
6827 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6828 player->index_bit, dig_side);
6829 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6830 player->index_bit, dig_side);
6835 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6839 if (ChangePage[newx][newy] != -1) /* delayed change */
6840 ChangeElement(newx, newy, ChangePage[newx][newy]);
6845 TestIfElementHitsCustomElement(newx, newy, direction);
6849 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6851 int hitting_element = Feld[newx][newy];
6853 /* !!! fix side (direction) orientation here and elsewhere !!! */
6854 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6858 if (IN_LEV_FIELD(nextx, nexty))
6860 int opposite_direction = MV_DIR_OPPOSITE(direction);
6861 int hitting_side = direction;
6862 int touched_side = opposite_direction;
6863 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6864 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6865 MovDir[nextx][nexty] != direction ||
6866 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6872 CheckElementChangeBySide(nextx, nexty, touched_element,
6873 CE_HIT_BY_SOMETHING, opposite_direction);
6875 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6876 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6878 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6880 struct ElementChangeInfo *change =
6881 &element_info[hitting_element].change_page[i];
6883 if (change->can_change &&
6884 change->has_event[CE_HITTING_X] &&
6885 change->trigger_side & touched_side &&
6886 change->trigger_element == touched_element)
6888 CheckElementChangeByPage(newx, newy, hitting_element,
6889 touched_element, CE_HITTING_X, i);
6895 if (IS_CUSTOM_ELEMENT(touched_element) &&
6896 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6898 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6900 struct ElementChangeInfo *change =
6901 &element_info[touched_element].change_page[i];
6903 if (change->can_change &&
6904 change->has_event[CE_HIT_BY_X] &&
6905 change->trigger_side & hitting_side &&
6906 change->trigger_element == hitting_element)
6908 CheckElementChangeByPage(nextx, nexty, touched_element,
6909 hitting_element, CE_HIT_BY_X,i);
6920 TestIfPlayerTouchesCustomElement(newx, newy);
6921 TestIfElementTouchesCustomElement(newx, newy);
6924 int AmoebeNachbarNr(int ax, int ay)
6927 int element = Feld[ax][ay];
6929 static int xy[4][2] =
6937 for (i = 0; i < NUM_DIRECTIONS; i++)
6939 int x = ax + xy[i][0];
6940 int y = ay + xy[i][1];
6942 if (!IN_LEV_FIELD(x, y))
6945 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6946 group_nr = AmoebaNr[x][y];
6952 void AmoebenVereinigen(int ax, int ay)
6954 int i, x, y, xx, yy;
6955 int new_group_nr = AmoebaNr[ax][ay];
6956 static int xy[4][2] =
6964 if (new_group_nr == 0)
6967 for (i = 0; i < NUM_DIRECTIONS; i++)
6972 if (!IN_LEV_FIELD(x, y))
6975 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6976 Feld[x][y] == EL_BD_AMOEBA ||
6977 Feld[x][y] == EL_AMOEBA_DEAD) &&
6978 AmoebaNr[x][y] != new_group_nr)
6980 int old_group_nr = AmoebaNr[x][y];
6982 if (old_group_nr == 0)
6985 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6986 AmoebaCnt[old_group_nr] = 0;
6987 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6988 AmoebaCnt2[old_group_nr] = 0;
6990 for (yy = 0; yy < lev_fieldy; yy++)
6992 for (xx = 0; xx < lev_fieldx; xx++)
6994 if (AmoebaNr[xx][yy] == old_group_nr)
6995 AmoebaNr[xx][yy] = new_group_nr;
7002 void AmoebeUmwandeln(int ax, int ay)
7006 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7008 int group_nr = AmoebaNr[ax][ay];
7013 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7014 printf("AmoebeUmwandeln(): This should never happen!\n");
7019 for (y = 0; y < lev_fieldy; y++)
7021 for (x = 0; x < lev_fieldx; x++)
7023 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7026 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7030 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7031 SND_AMOEBA_TURNING_TO_GEM :
7032 SND_AMOEBA_TURNING_TO_ROCK));
7037 static int xy[4][2] =
7045 for (i = 0; i < NUM_DIRECTIONS; i++)
7050 if (!IN_LEV_FIELD(x, y))
7053 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7055 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7056 SND_AMOEBA_TURNING_TO_GEM :
7057 SND_AMOEBA_TURNING_TO_ROCK));
7064 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7067 int group_nr = AmoebaNr[ax][ay];
7068 boolean done = FALSE;
7073 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7074 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7079 for (y = 0; y < lev_fieldy; y++)
7081 for (x = 0; x < lev_fieldx; x++)
7083 if (AmoebaNr[x][y] == group_nr &&
7084 (Feld[x][y] == EL_AMOEBA_DEAD ||
7085 Feld[x][y] == EL_BD_AMOEBA ||
7086 Feld[x][y] == EL_AMOEBA_GROWING))
7089 Feld[x][y] = new_element;
7090 InitField(x, y, FALSE);
7091 DrawLevelField(x, y);
7098 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7099 SND_BD_AMOEBA_TURNING_TO_ROCK :
7100 SND_BD_AMOEBA_TURNING_TO_GEM));
7103 void AmoebeWaechst(int x, int y)
7105 static unsigned long sound_delay = 0;
7106 static unsigned long sound_delay_value = 0;
7108 if (!MovDelay[x][y]) /* start new growing cycle */
7112 if (DelayReached(&sound_delay, sound_delay_value))
7115 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7117 if (Store[x][y] == EL_BD_AMOEBA)
7118 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7120 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7122 sound_delay_value = 30;
7126 if (MovDelay[x][y]) /* wait some time before growing bigger */
7129 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7131 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7132 6 - MovDelay[x][y]);
7134 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7137 if (!MovDelay[x][y])
7139 Feld[x][y] = Store[x][y];
7141 DrawLevelField(x, y);
7146 void AmoebaDisappearing(int x, int y)
7148 static unsigned long sound_delay = 0;
7149 static unsigned long sound_delay_value = 0;
7151 if (!MovDelay[x][y]) /* start new shrinking cycle */
7155 if (DelayReached(&sound_delay, sound_delay_value))
7156 sound_delay_value = 30;
7159 if (MovDelay[x][y]) /* wait some time before shrinking */
7162 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7164 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7165 6 - MovDelay[x][y]);
7167 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7170 if (!MovDelay[x][y])
7172 Feld[x][y] = EL_EMPTY;
7173 DrawLevelField(x, y);
7175 /* don't let mole enter this field in this cycle;
7176 (give priority to objects falling to this field from above) */
7182 void AmoebeAbleger(int ax, int ay)
7185 int element = Feld[ax][ay];
7186 int graphic = el2img(element);
7187 int newax = ax, neway = ay;
7188 static int xy[4][2] =
7196 if (!level.amoeba_speed)
7198 Feld[ax][ay] = EL_AMOEBA_DEAD;
7199 DrawLevelField(ax, ay);
7203 if (IS_ANIMATED(graphic))
7204 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7206 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7207 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7209 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7212 if (MovDelay[ax][ay])
7216 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7219 int x = ax + xy[start][0];
7220 int y = ay + xy[start][1];
7222 if (!IN_LEV_FIELD(x, y))
7226 if (IS_FREE(x, y) ||
7227 CAN_GROW_INTO(Feld[x][y]) ||
7228 Feld[x][y] == EL_QUICKSAND_EMPTY)
7234 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7235 if (IS_FREE(x, y) ||
7236 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7243 if (newax == ax && neway == ay)
7246 else /* normal or "filled" (BD style) amoeba */
7249 boolean waiting_for_player = FALSE;
7251 for (i = 0; i < NUM_DIRECTIONS; i++)
7253 int j = (start + i) % 4;
7254 int x = ax + xy[j][0];
7255 int y = ay + xy[j][1];
7257 if (!IN_LEV_FIELD(x, y))
7261 if (IS_FREE(x, y) ||
7262 CAN_GROW_INTO(Feld[x][y]) ||
7263 Feld[x][y] == EL_QUICKSAND_EMPTY)
7270 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7271 if (IS_FREE(x, y) ||
7272 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7279 else if (IS_PLAYER(x, y))
7280 waiting_for_player = TRUE;
7283 if (newax == ax && neway == ay) /* amoeba cannot grow */
7286 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7288 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7291 Feld[ax][ay] = EL_AMOEBA_DEAD;
7292 DrawLevelField(ax, ay);
7293 AmoebaCnt[AmoebaNr[ax][ay]]--;
7295 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7297 if (element == EL_AMOEBA_FULL)
7298 AmoebeUmwandeln(ax, ay);
7299 else if (element == EL_BD_AMOEBA)
7300 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7305 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7307 /* amoeba gets larger by growing in some direction */
7309 int new_group_nr = AmoebaNr[ax][ay];
7312 if (new_group_nr == 0)
7314 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7315 printf("AmoebeAbleger(): This should never happen!\n");
7320 AmoebaNr[newax][neway] = new_group_nr;
7321 AmoebaCnt[new_group_nr]++;
7322 AmoebaCnt2[new_group_nr]++;
7324 /* if amoeba touches other amoeba(s) after growing, unify them */
7325 AmoebenVereinigen(newax, neway);
7327 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7329 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7335 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7336 (neway == lev_fieldy - 1 && newax != ax))
7338 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7339 Store[newax][neway] = element;
7341 else if (neway == ay)
7343 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7345 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7347 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7352 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7353 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7354 Store[ax][ay] = EL_AMOEBA_DROP;
7355 ContinueMoving(ax, ay);
7359 DrawLevelField(newax, neway);
7362 void Life(int ax, int ay)
7365 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7367 int element = Feld[ax][ay];
7368 int graphic = el2img(element);
7369 boolean changed = FALSE;
7371 if (IS_ANIMATED(graphic))
7372 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7377 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7378 MovDelay[ax][ay] = life_time;
7380 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7383 if (MovDelay[ax][ay])
7387 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7389 int xx = ax+x1, yy = ay+y1;
7392 if (!IN_LEV_FIELD(xx, yy))
7395 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7397 int x = xx+x2, y = yy+y2;
7399 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7402 if (((Feld[x][y] == element ||
7403 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7405 (IS_FREE(x, y) && Stop[x][y]))
7409 if (xx == ax && yy == ay) /* field in the middle */
7411 if (nachbarn < life[0] || nachbarn > life[1])
7413 Feld[xx][yy] = EL_EMPTY;
7415 DrawLevelField(xx, yy);
7416 Stop[xx][yy] = TRUE;
7421 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7422 { /* free border field */
7423 if (nachbarn >= life[2] && nachbarn <= life[3])
7425 Feld[xx][yy] = element;
7426 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7428 DrawLevelField(xx, yy);
7429 Stop[xx][yy] = TRUE;
7434 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7435 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7436 { /* free border field */
7437 if (nachbarn >= life[2] && nachbarn <= life[3])
7439 Feld[xx][yy] = element;
7440 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7442 DrawLevelField(xx, yy);
7443 Stop[xx][yy] = TRUE;
7451 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7452 SND_GAME_OF_LIFE_GROWING);
7455 static void InitRobotWheel(int x, int y)
7457 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7460 static void RunRobotWheel(int x, int y)
7462 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7465 static void StopRobotWheel(int x, int y)
7467 if (ZX == x && ZY == y)
7471 static void InitTimegateWheel(int x, int y)
7474 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7476 /* another brainless, "type style" bug ... :-( */
7477 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7481 static void RunTimegateWheel(int x, int y)
7483 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7486 void CheckExit(int x, int y)
7488 if (local_player->gems_still_needed > 0 ||
7489 local_player->sokobanfields_still_needed > 0 ||
7490 local_player->lights_still_needed > 0)
7492 int element = Feld[x][y];
7493 int graphic = el2img(element);
7495 if (IS_ANIMATED(graphic))
7496 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7501 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7504 Feld[x][y] = EL_EXIT_OPENING;
7506 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7509 void CheckExitSP(int x, int y)
7511 if (local_player->gems_still_needed > 0)
7513 int element = Feld[x][y];
7514 int graphic = el2img(element);
7516 if (IS_ANIMATED(graphic))
7517 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7522 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7525 Feld[x][y] = EL_SP_EXIT_OPENING;
7527 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7530 static void CloseAllOpenTimegates()
7534 for (y = 0; y < lev_fieldy; y++)
7536 for (x = 0; x < lev_fieldx; x++)
7538 int element = Feld[x][y];
7540 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7542 Feld[x][y] = EL_TIMEGATE_CLOSING;
7544 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7546 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7553 void EdelsteinFunkeln(int x, int y)
7555 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7558 if (Feld[x][y] == EL_BD_DIAMOND)
7561 if (MovDelay[x][y] == 0) /* next animation frame */
7562 MovDelay[x][y] = 11 * !SimpleRND(500);
7564 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7568 if (setup.direct_draw && MovDelay[x][y])
7569 SetDrawtoField(DRAW_BUFFERED);
7571 DrawLevelElementAnimation(x, y, Feld[x][y]);
7573 if (MovDelay[x][y] != 0)
7575 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7576 10 - MovDelay[x][y]);
7578 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7580 if (setup.direct_draw)
7584 dest_x = FX + SCREENX(x) * TILEX;
7585 dest_y = FY + SCREENY(y) * TILEY;
7587 BlitBitmap(drawto_field, window,
7588 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7589 SetDrawtoField(DRAW_DIRECT);
7595 void MauerWaechst(int x, int y)
7599 if (!MovDelay[x][y]) /* next animation frame */
7600 MovDelay[x][y] = 3 * delay;
7602 if (MovDelay[x][y]) /* wait some time before next frame */
7606 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7608 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7609 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7611 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7614 if (!MovDelay[x][y])
7616 if (MovDir[x][y] == MV_LEFT)
7618 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7619 DrawLevelField(x - 1, y);
7621 else if (MovDir[x][y] == MV_RIGHT)
7623 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7624 DrawLevelField(x + 1, y);
7626 else if (MovDir[x][y] == MV_UP)
7628 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7629 DrawLevelField(x, y - 1);
7633 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7634 DrawLevelField(x, y + 1);
7637 Feld[x][y] = Store[x][y];
7639 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7640 DrawLevelField(x, y);
7645 void MauerAbleger(int ax, int ay)
7647 int element = Feld[ax][ay];
7648 int graphic = el2img(element);
7649 boolean oben_frei = FALSE, unten_frei = FALSE;
7650 boolean links_frei = FALSE, rechts_frei = FALSE;
7651 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7652 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7653 boolean new_wall = FALSE;
7655 if (IS_ANIMATED(graphic))
7656 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7658 if (!MovDelay[ax][ay]) /* start building new wall */
7659 MovDelay[ax][ay] = 6;
7661 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7664 if (MovDelay[ax][ay])
7668 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7670 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7672 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7674 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7677 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7678 element == EL_EXPANDABLE_WALL_ANY)
7682 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7683 Store[ax][ay-1] = element;
7684 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7685 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7686 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7687 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7692 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7693 Store[ax][ay+1] = element;
7694 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7695 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7696 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7697 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7702 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7703 element == EL_EXPANDABLE_WALL_ANY ||
7704 element == EL_EXPANDABLE_WALL)
7708 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7709 Store[ax-1][ay] = element;
7710 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7711 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7712 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7713 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7719 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7720 Store[ax+1][ay] = element;
7721 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7722 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7723 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7724 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7729 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7730 DrawLevelField(ax, ay);
7732 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7734 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7735 unten_massiv = TRUE;
7736 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7737 links_massiv = TRUE;
7738 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7739 rechts_massiv = TRUE;
7741 if (((oben_massiv && unten_massiv) ||
7742 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7743 element == EL_EXPANDABLE_WALL) &&
7744 ((links_massiv && rechts_massiv) ||
7745 element == EL_EXPANDABLE_WALL_VERTICAL))
7746 Feld[ax][ay] = EL_WALL;
7750 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7752 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7756 void CheckForDragon(int x, int y)
7759 boolean dragon_found = FALSE;
7760 static int xy[4][2] =
7768 for (i = 0; i < NUM_DIRECTIONS; i++)
7770 for (j = 0; j < 4; j++)
7772 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7774 if (IN_LEV_FIELD(xx, yy) &&
7775 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7777 if (Feld[xx][yy] == EL_DRAGON)
7778 dragon_found = TRUE;
7787 for (i = 0; i < NUM_DIRECTIONS; i++)
7789 for (j = 0; j < 3; j++)
7791 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7793 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7795 Feld[xx][yy] = EL_EMPTY;
7796 DrawLevelField(xx, yy);
7805 static void InitBuggyBase(int x, int y)
7807 int element = Feld[x][y];
7808 int activating_delay = FRAMES_PER_SECOND / 4;
7811 (element == EL_SP_BUGGY_BASE ?
7812 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7813 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7815 element == EL_SP_BUGGY_BASE_ACTIVE ?
7816 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7819 static void WarnBuggyBase(int x, int y)
7822 static int xy[4][2] =
7830 for (i = 0; i < NUM_DIRECTIONS; i++)
7832 int xx = x + xy[i][0], yy = y + xy[i][1];
7834 if (IS_PLAYER(xx, yy))
7836 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7843 static void InitTrap(int x, int y)
7845 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7848 static void ActivateTrap(int x, int y)
7850 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7853 static void ChangeActiveTrap(int x, int y)
7855 int graphic = IMG_TRAP_ACTIVE;
7857 /* if new animation frame was drawn, correct crumbled sand border */
7858 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7859 DrawLevelFieldCrumbledSand(x, y);
7862 static void ChangeElementNowExt(int x, int y, int target_element)
7864 int previous_move_direction = MovDir[x][y];
7866 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7867 IS_WALKABLE(Feld[x][y]));
7869 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7870 IS_WALKABLE(Feld[x][y]) &&
7874 /* check if element under player changes from accessible to unaccessible
7875 (needed for special case of dropping element which then changes) */
7876 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7877 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7880 printf("::: BOOOM! [%d, '%s']\n", target_element,
7881 element_info[target_element].token_name);
7893 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7894 RemoveMovingField(x, y);
7898 Feld[x][y] = target_element;
7901 Feld[x][y] = target_element;
7904 ResetGfxAnimation(x, y);
7905 ResetRandomAnimationValue(x, y);
7907 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7908 MovDir[x][y] = previous_move_direction;
7911 InitField_WithBug1(x, y, FALSE);
7913 InitField(x, y, FALSE);
7914 if (CAN_MOVE(Feld[x][y]))
7918 DrawLevelField(x, y);
7920 if (GFX_CRUMBLED(Feld[x][y]))
7921 DrawLevelFieldCrumbledSandNeighbours(x, y);
7925 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7929 TestIfBadThingTouchesHero(x, y);
7930 TestIfPlayerTouchesCustomElement(x, y);
7931 TestIfElementTouchesCustomElement(x, y);
7934 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7935 if (ELEM_IS_PLAYER(target_element))
7936 RelocatePlayer(x, y, target_element);
7939 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7941 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7945 TestIfBadThingTouchesHero(x, y);
7946 TestIfPlayerTouchesCustomElement(x, y);
7947 TestIfElementTouchesCustomElement(x, y);
7951 static boolean ChangeElementNow(int x, int y, int element, int page)
7953 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7955 int old_element = Feld[x][y];
7957 /* always use default change event to prevent running into a loop */
7958 if (ChangeEvent[x][y] == -1)
7959 ChangeEvent[x][y] = CE_DELAY;
7961 if (ChangeEvent[x][y] == CE_DELAY)
7963 /* reset actual trigger element and player */
7964 change->actual_trigger_element = EL_EMPTY;
7965 change->actual_trigger_player = EL_PLAYER_1;
7969 /* do not change any elements that have already changed in this frame */
7973 /* do not change already changed elements with same change event */
7974 if (Changed[x][y] & ChangeEvent[x][y])
7979 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7981 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7985 /* !!! indirect change before direct change !!! */
7986 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
7989 if (change->explode)
7996 if (change->use_target_content)
7998 boolean complete_replace = TRUE;
7999 boolean can_replace[3][3];
8002 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8005 boolean is_walkable;
8006 boolean is_diggable;
8007 boolean is_collectible;
8008 boolean is_removable;
8009 boolean is_destructible;
8010 int ex = x + xx - 1;
8011 int ey = y + yy - 1;
8012 int content_element = change->target_content[xx][yy];
8015 can_replace[xx][yy] = TRUE;
8017 if (ex == x && ey == y) /* do not check changing element itself */
8020 if (content_element == EL_EMPTY_SPACE)
8022 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8027 if (!IN_LEV_FIELD(ex, ey))
8029 can_replace[xx][yy] = FALSE;
8030 complete_replace = FALSE;
8036 if (Changed[ex][ey]) /* do not change already changed elements */
8038 can_replace[xx][yy] = FALSE;
8039 complete_replace = FALSE;
8047 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8048 e = MovingOrBlocked2Element(ex, ey);
8053 is_empty = (IS_FREE(ex, ey) ||
8054 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8055 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8056 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8060 is_empty = (IS_FREE(ex, ey) ||
8061 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8063 is_empty = (IS_FREE(ex, ey) ||
8064 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8069 is_walkable = (is_empty || IS_WALKABLE(e));
8070 is_diggable = (is_empty || IS_DIGGABLE(e));
8071 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8072 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8073 is_removable = (is_diggable || is_collectible);
8075 can_replace[xx][yy] =
8076 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8077 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8078 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8079 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8080 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8081 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8082 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8084 if (!can_replace[xx][yy])
8085 complete_replace = FALSE;
8087 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8088 IS_WALKABLE(content_element)));
8090 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8092 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8095 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8096 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8097 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8099 can_replace[xx][yy] = FALSE;
8100 complete_replace = FALSE;
8105 if (!change->only_if_complete || complete_replace)
8107 boolean something_has_changed = FALSE;
8109 if (change->only_if_complete && change->use_random_replace &&
8110 RND(100) < change->random_percentage)
8113 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8115 int ex = x + xx - 1;
8116 int ey = y + yy - 1;
8117 int content_element;
8119 if (can_replace[xx][yy] && (!change->use_random_replace ||
8120 RND(100) < change->random_percentage))
8122 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8123 RemoveMovingField(ex, ey);
8125 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8127 content_element = change->target_content[xx][yy];
8128 target_element = GET_TARGET_ELEMENT(content_element, change);
8130 ChangeElementNowExt(ex, ey, target_element);
8132 something_has_changed = TRUE;
8134 /* for symmetry reasons, freeze newly created border elements */
8135 if (ex != x || ey != y)
8136 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8140 if (something_has_changed)
8141 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8146 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8148 ChangeElementNowExt(x, y, target_element);
8150 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8154 /* this uses direct change before indirect change */
8155 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8161 static void ChangeElement(int x, int y, int page)
8163 int element = MovingOrBlocked2Element(x, y);
8164 struct ElementInfo *ei = &element_info[element];
8165 struct ElementChangeInfo *change = &ei->change_page[page];
8168 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8171 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8172 x, y, element, element_info[element].token_name);
8173 printf("ChangeElement(): This should never happen!\n");
8178 /* this can happen with classic bombs on walkable, changing elements */
8179 if (!CAN_CHANGE(element))
8182 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8183 ChangeDelay[x][y] = 0;
8189 if (ChangeDelay[x][y] == 0) /* initialize element change */
8191 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8192 RND(change->delay_random * change->delay_frames)) + 1;
8194 ResetGfxAnimation(x, y);
8195 ResetRandomAnimationValue(x, y);
8197 if (change->pre_change_function)
8198 change->pre_change_function(x, y);
8201 ChangeDelay[x][y]--;
8203 if (ChangeDelay[x][y] != 0) /* continue element change */
8205 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8207 if (IS_ANIMATED(graphic))
8208 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8210 if (change->change_function)
8211 change->change_function(x, y);
8213 else /* finish element change */
8215 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8217 page = ChangePage[x][y];
8218 ChangePage[x][y] = -1;
8220 change = &ei->change_page[page];
8224 if (IS_MOVING(x, y) && !change->explode)
8226 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8229 ChangeDelay[x][y] = 1; /* try change after next move step */
8230 ChangePage[x][y] = page; /* remember page to use for change */
8235 if (ChangeElementNow(x, y, element, page))
8237 if (change->post_change_function)
8238 change->post_change_function(x, y);
8243 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8244 int trigger_element,
8251 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8253 if (!(trigger_events[trigger_element][trigger_event]))
8256 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8258 int element = EL_CUSTOM_START + i;
8260 boolean change_element = FALSE;
8263 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8266 for (j = 0; j < element_info[element].num_change_pages; j++)
8268 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8270 if (change->can_change &&
8271 change->has_event[trigger_event] &&
8272 change->trigger_side & trigger_side &&
8273 change->trigger_player & trigger_player &&
8274 change->trigger_page & trigger_page_bits &&
8275 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8278 if (!(change->has_event[trigger_event]))
8279 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8280 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8283 change_element = TRUE;
8286 change->actual_trigger_element = trigger_element;
8287 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8293 if (!change_element)
8296 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8299 if (x == lx && y == ly) /* do not change trigger element itself */
8303 if (Feld[x][y] == element)
8305 ChangeDelay[x][y] = 1;
8306 ChangeEvent[x][y] = trigger_event;
8307 ChangeElement(x, y, page);
8315 static boolean CheckElementChangeExt(int x, int y,
8317 int trigger_element,
8323 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8326 if (Feld[x][y] == EL_BLOCKED)
8328 Blocked2Moving(x, y, &x, &y);
8329 element = Feld[x][y];
8333 if (Feld[x][y] != element) /* check if element has already changed */
8336 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8337 Feld[x][y], element_info[Feld[x][y]].token_name,
8338 element, element_info[element].token_name,
8347 if (trigger_page < 0)
8349 boolean change_element = FALSE;
8352 for (i = 0; i < element_info[element].num_change_pages; i++)
8354 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8356 if (change->can_change &&
8357 change->has_event[trigger_event] &&
8358 change->trigger_side & trigger_side &&
8359 change->trigger_player & trigger_player)
8361 change_element = TRUE;
8364 change->actual_trigger_element = trigger_element;
8365 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8371 if (!change_element)
8376 struct ElementInfo *ei = &element_info[element];
8377 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8379 change->actual_trigger_element = trigger_element;
8380 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8385 /* !!! this check misses pages with same event, but different side !!! */
8387 if (trigger_page < 0)
8388 trigger_page = element_info[element].event_page_nr[trigger_event];
8390 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8394 ChangeDelay[x][y] = 1;
8395 ChangeEvent[x][y] = trigger_event;
8396 ChangeElement(x, y, trigger_page);
8401 static void PlayPlayerSound(struct PlayerInfo *player)
8403 int jx = player->jx, jy = player->jy;
8404 int element = player->element_nr;
8405 int last_action = player->last_action_waiting;
8406 int action = player->action_waiting;
8408 if (player->is_waiting)
8410 if (action != last_action)
8411 PlayLevelSoundElementAction(jx, jy, element, action);
8413 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8417 if (action != last_action)
8418 StopSound(element_info[element].sound[last_action]);
8420 if (last_action == ACTION_SLEEPING)
8421 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8425 static void PlayAllPlayersSound()
8429 for (i = 0; i < MAX_PLAYERS; i++)
8430 if (stored_player[i].active)
8431 PlayPlayerSound(&stored_player[i]);
8434 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8436 boolean last_waiting = player->is_waiting;
8437 int move_dir = player->MovDir;
8439 player->last_action_waiting = player->action_waiting;
8443 if (!last_waiting) /* not waiting -> waiting */
8445 player->is_waiting = TRUE;
8447 player->frame_counter_bored =
8449 game.player_boring_delay_fixed +
8450 SimpleRND(game.player_boring_delay_random);
8451 player->frame_counter_sleeping =
8453 game.player_sleeping_delay_fixed +
8454 SimpleRND(game.player_sleeping_delay_random);
8456 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8459 if (game.player_sleeping_delay_fixed +
8460 game.player_sleeping_delay_random > 0 &&
8461 player->anim_delay_counter == 0 &&
8462 player->post_delay_counter == 0 &&
8463 FrameCounter >= player->frame_counter_sleeping)
8464 player->is_sleeping = TRUE;
8465 else if (game.player_boring_delay_fixed +
8466 game.player_boring_delay_random > 0 &&
8467 FrameCounter >= player->frame_counter_bored)
8468 player->is_bored = TRUE;
8470 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8471 player->is_bored ? ACTION_BORING :
8474 if (player->is_sleeping)
8476 if (player->num_special_action_sleeping > 0)
8478 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8480 int last_special_action = player->special_action_sleeping;
8481 int num_special_action = player->num_special_action_sleeping;
8482 int special_action =
8483 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8484 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8485 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8486 last_special_action + 1 : ACTION_SLEEPING);
8487 int special_graphic =
8488 el_act_dir2img(player->element_nr, special_action, move_dir);
8490 player->anim_delay_counter =
8491 graphic_info[special_graphic].anim_delay_fixed +
8492 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8493 player->post_delay_counter =
8494 graphic_info[special_graphic].post_delay_fixed +
8495 SimpleRND(graphic_info[special_graphic].post_delay_random);
8497 player->special_action_sleeping = special_action;
8500 if (player->anim_delay_counter > 0)
8502 player->action_waiting = player->special_action_sleeping;
8503 player->anim_delay_counter--;
8505 else if (player->post_delay_counter > 0)
8507 player->post_delay_counter--;
8511 else if (player->is_bored)
8513 if (player->num_special_action_bored > 0)
8515 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8517 int special_action =
8518 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8519 int special_graphic =
8520 el_act_dir2img(player->element_nr, special_action, move_dir);
8522 player->anim_delay_counter =
8523 graphic_info[special_graphic].anim_delay_fixed +
8524 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8525 player->post_delay_counter =
8526 graphic_info[special_graphic].post_delay_fixed +
8527 SimpleRND(graphic_info[special_graphic].post_delay_random);
8529 player->special_action_bored = special_action;
8532 if (player->anim_delay_counter > 0)
8534 player->action_waiting = player->special_action_bored;
8535 player->anim_delay_counter--;
8537 else if (player->post_delay_counter > 0)
8539 player->post_delay_counter--;
8544 else if (last_waiting) /* waiting -> not waiting */
8546 player->is_waiting = FALSE;
8547 player->is_bored = FALSE;
8548 player->is_sleeping = FALSE;
8550 player->frame_counter_bored = -1;
8551 player->frame_counter_sleeping = -1;
8553 player->anim_delay_counter = 0;
8554 player->post_delay_counter = 0;
8556 player->action_waiting = ACTION_DEFAULT;
8558 player->special_action_bored = ACTION_DEFAULT;
8559 player->special_action_sleeping = ACTION_DEFAULT;
8564 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8567 static byte stored_player_action[MAX_PLAYERS];
8568 static int num_stored_actions = 0;
8570 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8571 int left = player_action & JOY_LEFT;
8572 int right = player_action & JOY_RIGHT;
8573 int up = player_action & JOY_UP;
8574 int down = player_action & JOY_DOWN;
8575 int button1 = player_action & JOY_BUTTON_1;
8576 int button2 = player_action & JOY_BUTTON_2;
8577 int dx = (left ? -1 : right ? 1 : 0);
8578 int dy = (up ? -1 : down ? 1 : 0);
8581 stored_player_action[player->index_nr] = 0;
8582 num_stored_actions++;
8586 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8589 if (!player->active || tape.pausing)
8593 printf("::: [%d %d %d %d] [%d %d]\n",
8594 left, right, up, down, button1, button2);
8600 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8605 if (player->MovPos == 0)
8606 CheckGravityMovement(player);
8609 snapped = SnapField(player, dx, dy);
8613 dropped = DropElement(player);
8615 moved = MovePlayer(player, dx, dy);
8618 if (tape.single_step && tape.recording && !tape.pausing)
8620 if (button1 || (dropped && !moved))
8622 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8623 SnapField(player, 0, 0); /* stop snapping */
8627 SetPlayerWaiting(player, FALSE);
8630 return player_action;
8632 stored_player_action[player->index_nr] = player_action;
8638 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8641 /* no actions for this player (no input at player's configured device) */
8643 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8644 SnapField(player, 0, 0);
8645 CheckGravityMovementWhenNotMoving(player);
8647 if (player->MovPos == 0)
8648 SetPlayerWaiting(player, TRUE);
8650 if (player->MovPos == 0) /* needed for tape.playing */
8651 player->is_moving = FALSE;
8653 player->is_dropping = FALSE;
8659 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8661 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8663 TapeRecordAction(stored_player_action);
8664 num_stored_actions = 0;
8671 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8673 static byte stored_player_action[MAX_PLAYERS];
8674 static int num_stored_actions = 0;
8675 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8676 int left = player_action & JOY_LEFT;
8677 int right = player_action & JOY_RIGHT;
8678 int up = player_action & JOY_UP;
8679 int down = player_action & JOY_DOWN;
8680 int button1 = player_action & JOY_BUTTON_1;
8681 int button2 = player_action & JOY_BUTTON_2;
8682 int dx = (left ? -1 : right ? 1 : 0);
8683 int dy = (up ? -1 : down ? 1 : 0);
8685 stored_player_action[player->index_nr] = 0;
8686 num_stored_actions++;
8688 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8690 if (!player->active || tape.pausing)
8695 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8698 snapped = SnapField(player, dx, dy);
8702 dropped = DropElement(player);
8704 moved = MovePlayer(player, dx, dy);
8707 if (tape.single_step && tape.recording && !tape.pausing)
8709 if (button1 || (dropped && !moved))
8711 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8712 SnapField(player, 0, 0); /* stop snapping */
8716 stored_player_action[player->index_nr] = player_action;
8720 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8722 /* no actions for this player (no input at player's configured device) */
8724 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8725 SnapField(player, 0, 0);
8726 CheckGravityMovementWhenNotMoving(player);
8728 if (player->MovPos == 0)
8729 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8731 if (player->MovPos == 0) /* needed for tape.playing */
8732 player->is_moving = FALSE;
8735 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8737 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8739 TapeRecordAction(stored_player_action);
8740 num_stored_actions = 0;
8745 void AdvanceFrameAndPlayerCounters(int player_nr)
8749 /* advance frame counters (global frame counter and time frame counter) */
8753 /* advance player counters (counters for move delay, move animation etc.) */
8754 for (i = 0; i < MAX_PLAYERS; i++)
8756 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8758 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8760 if (!advance_player_counters) /* not all players may be affected */
8763 stored_player[i].Frame += move_frames;
8765 if (stored_player[i].MovPos != 0)
8766 stored_player[i].StepFrame += move_frames;
8768 #if USE_NEW_MOVE_DELAY
8769 if (stored_player[i].move_delay > 0)
8770 stored_player[i].move_delay--;
8773 #if USE_NEW_PUSH_DELAY
8774 /* due to bugs in previous versions, counter must count up, not down */
8775 if (stored_player[i].push_delay != -1)
8776 stored_player[i].push_delay++;
8779 if (stored_player[i].drop_delay > 0)
8780 stored_player[i].drop_delay--;
8786 static unsigned long game_frame_delay = 0;
8787 unsigned long game_frame_delay_value;
8788 int magic_wall_x = 0, magic_wall_y = 0;
8789 int i, x, y, element, graphic;
8790 byte *recorded_player_action;
8791 byte summarized_player_action = 0;
8793 byte tape_action[MAX_PLAYERS];
8796 if (game_status != GAME_MODE_PLAYING)
8799 game_frame_delay_value =
8800 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8802 if (tape.playing && tape.warp_forward && !tape.pausing)
8803 game_frame_delay_value = 0;
8805 /* ---------- main game synchronization point ---------- */
8807 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8809 if (network_playing && !network_player_action_received)
8813 printf("DEBUG: try to get network player actions in time\n");
8817 #if defined(NETWORK_AVALIABLE)
8818 /* last chance to get network player actions without main loop delay */
8822 if (game_status != GAME_MODE_PLAYING)
8825 if (!network_player_action_received)
8829 printf("DEBUG: failed to get network player actions in time\n");
8840 printf("::: getting new tape action [%d]\n", FrameCounter);
8843 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8846 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8847 if (recorded_player_action == NULL && tape.pausing)
8852 printf("::: %d\n", stored_player[0].action);
8856 if (recorded_player_action != NULL)
8857 for (i = 0; i < MAX_PLAYERS; i++)
8858 stored_player[i].action = recorded_player_action[i];
8861 for (i = 0; i < MAX_PLAYERS; i++)
8863 summarized_player_action |= stored_player[i].action;
8865 if (!network_playing)
8866 stored_player[i].effective_action = stored_player[i].action;
8869 #if defined(NETWORK_AVALIABLE)
8870 if (network_playing)
8871 SendToServer_MovePlayer(summarized_player_action);
8874 if (!options.network && !setup.team_mode)
8875 local_player->effective_action = summarized_player_action;
8878 if (recorded_player_action != NULL)
8879 for (i = 0; i < MAX_PLAYERS; i++)
8880 stored_player[i].effective_action = recorded_player_action[i];
8884 for (i = 0; i < MAX_PLAYERS; i++)
8886 tape_action[i] = stored_player[i].effective_action;
8888 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8889 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8892 /* only save actions from input devices, but not programmed actions */
8894 TapeRecordAction(tape_action);
8897 for (i = 0; i < MAX_PLAYERS; i++)
8899 int actual_player_action = stored_player[i].effective_action;
8902 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8903 - rnd_equinox_tetrachloride 048
8904 - rnd_equinox_tetrachloride_ii 096
8905 - rnd_emanuel_schmieg 002
8906 - doctor_sloan_ww 001, 020
8908 if (stored_player[i].MovPos == 0)
8909 CheckGravityMovement(&stored_player[i]);
8913 /* overwrite programmed action with tape action */
8914 if (stored_player[i].programmed_action)
8915 actual_player_action = stored_player[i].programmed_action;
8919 if (stored_player[i].programmed_action)
8920 printf("::: %d\n", stored_player[i].programmed_action);
8923 if (recorded_player_action)
8926 if (stored_player[i].programmed_action &&
8927 stored_player[i].programmed_action != recorded_player_action[i])
8928 printf("::: %d: %d <-> %d\n", i,
8929 stored_player[i].programmed_action, recorded_player_action[i]);
8933 actual_player_action = recorded_player_action[i];
8938 /* overwrite tape action with programmed action */
8939 if (stored_player[i].programmed_action)
8940 actual_player_action = stored_player[i].programmed_action;
8945 printf("::: action: %d: %x [%d]\n",
8946 stored_player[i].MovPos, actual_player_action, FrameCounter);
8950 PlayerActions(&stored_player[i], actual_player_action);
8952 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8954 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8955 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8958 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8963 TapeRecordAction(tape_action);
8966 network_player_action_received = FALSE;
8968 ScrollScreen(NULL, SCROLL_GO_ON);
8974 for (i = 0; i < MAX_PLAYERS; i++)
8975 stored_player[i].Frame++;
8979 /* for backwards compatibility, the following code emulates a fixed bug that
8980 occured when pushing elements (causing elements that just made their last
8981 pushing step to already (if possible) make their first falling step in the
8982 same game frame, which is bad); this code is also needed to use the famous
8983 "spring push bug" which is used in older levels and might be wanted to be
8984 used also in newer levels, but in this case the buggy pushing code is only
8985 affecting the "spring" element and no other elements */
8988 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8990 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8993 for (i = 0; i < MAX_PLAYERS; i++)
8995 struct PlayerInfo *player = &stored_player[i];
9000 if (player->active && player->is_pushing && player->is_moving &&
9002 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9003 Feld[x][y] == EL_SPRING))
9005 if (player->active && player->is_pushing && player->is_moving &&
9009 ContinueMoving(x, y);
9011 /* continue moving after pushing (this is actually a bug) */
9012 if (!IS_MOVING(x, y))
9021 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9023 Changed[x][y] = FALSE;
9024 ChangeEvent[x][y] = -1;
9026 #if USE_NEW_BLOCK_STYLE
9027 /* this must be handled before main playfield loop */
9028 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9031 if (MovDelay[x][y] <= 0)
9037 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9039 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9040 printf("GameActions(): This should never happen!\n");
9042 ChangePage[x][y] = -1;
9047 if (WasJustMoving[x][y] > 0)
9048 WasJustMoving[x][y]--;
9049 if (WasJustFalling[x][y] > 0)
9050 WasJustFalling[x][y]--;
9051 if (CheckCollision[x][y] > 0)
9052 CheckCollision[x][y]--;
9057 /* reset finished pushing action (not done in ContinueMoving() to allow
9058 continous pushing animation for elements with zero push delay) */
9059 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9061 ResetGfxAnimation(x, y);
9062 DrawLevelField(x, y);
9067 if (IS_BLOCKED(x, y))
9071 Blocked2Moving(x, y, &oldx, &oldy);
9072 if (!IS_MOVING(oldx, oldy))
9074 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9075 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9076 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9077 printf("GameActions(): This should never happen!\n");
9083 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9085 element = Feld[x][y];
9087 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9089 graphic = el2img(element);
9095 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9097 element = graphic = 0;
9101 if (graphic_info[graphic].anim_global_sync)
9102 GfxFrame[x][y] = FrameCounter;
9104 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9105 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9106 ResetRandomAnimationValue(x, y);
9108 SetRandomAnimationValue(x, y);
9111 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9114 if (IS_INACTIVE(element))
9116 if (IS_ANIMATED(graphic))
9117 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9123 /* this may take place after moving, so 'element' may have changed */
9125 if (IS_CHANGING(x, y))
9127 if (IS_CHANGING(x, y) &&
9128 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9132 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9133 element_info[element].event_page_nr[CE_DELAY]);
9135 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9138 element = Feld[x][y];
9139 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9143 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9148 element = Feld[x][y];
9149 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9151 if (element == EL_MOLE)
9152 printf("::: %d, %d, %d [%d]\n",
9153 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9157 if (element == EL_YAMYAM)
9158 printf("::: %d, %d, %d\n",
9159 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9163 if (IS_ANIMATED(graphic) &&
9167 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9170 if (element == EL_BUG)
9171 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9175 if (element == EL_MOLE)
9176 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9180 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9181 EdelsteinFunkeln(x, y);
9183 else if ((element == EL_ACID ||
9184 element == EL_EXIT_OPEN ||
9185 element == EL_SP_EXIT_OPEN ||
9186 element == EL_SP_TERMINAL ||
9187 element == EL_SP_TERMINAL_ACTIVE ||
9188 element == EL_EXTRA_TIME ||
9189 element == EL_SHIELD_NORMAL ||
9190 element == EL_SHIELD_DEADLY) &&
9191 IS_ANIMATED(graphic))
9192 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9193 else if (IS_MOVING(x, y))
9194 ContinueMoving(x, y);
9195 else if (IS_ACTIVE_BOMB(element))
9196 CheckDynamite(x, y);
9198 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9199 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9201 else if (element == EL_AMOEBA_GROWING)
9202 AmoebeWaechst(x, y);
9203 else if (element == EL_AMOEBA_SHRINKING)
9204 AmoebaDisappearing(x, y);
9206 #if !USE_NEW_AMOEBA_CODE
9207 else if (IS_AMOEBALIVE(element))
9208 AmoebeAbleger(x, y);
9211 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9213 else if (element == EL_EXIT_CLOSED)
9215 else if (element == EL_SP_EXIT_CLOSED)
9217 else if (element == EL_EXPANDABLE_WALL_GROWING)
9219 else if (element == EL_EXPANDABLE_WALL ||
9220 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9221 element == EL_EXPANDABLE_WALL_VERTICAL ||
9222 element == EL_EXPANDABLE_WALL_ANY)
9224 else if (element == EL_FLAMES)
9225 CheckForDragon(x, y);
9227 else if (IS_AUTO_CHANGING(element))
9228 ChangeElement(x, y);
9230 else if (element == EL_EXPLOSION)
9231 ; /* drawing of correct explosion animation is handled separately */
9232 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9233 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9236 /* this may take place after moving, so 'element' may have changed */
9237 if (IS_AUTO_CHANGING(Feld[x][y]))
9238 ChangeElement(x, y);
9241 if (IS_BELT_ACTIVE(element))
9242 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9244 if (game.magic_wall_active)
9246 int jx = local_player->jx, jy = local_player->jy;
9248 /* play the element sound at the position nearest to the player */
9249 if ((element == EL_MAGIC_WALL_FULL ||
9250 element == EL_MAGIC_WALL_ACTIVE ||
9251 element == EL_MAGIC_WALL_EMPTYING ||
9252 element == EL_BD_MAGIC_WALL_FULL ||
9253 element == EL_BD_MAGIC_WALL_ACTIVE ||
9254 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9255 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9263 #if USE_NEW_AMOEBA_CODE
9264 /* new experimental amoeba growth stuff */
9266 if (!(FrameCounter % 8))
9269 static unsigned long random = 1684108901;
9271 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9274 x = (random >> 10) % lev_fieldx;
9275 y = (random >> 20) % lev_fieldy;
9277 x = RND(lev_fieldx);
9278 y = RND(lev_fieldy);
9280 element = Feld[x][y];
9283 if (!IS_PLAYER(x,y) &&
9284 (element == EL_EMPTY ||
9285 CAN_GROW_INTO(element) ||
9286 element == EL_QUICKSAND_EMPTY ||
9287 element == EL_ACID_SPLASH_LEFT ||
9288 element == EL_ACID_SPLASH_RIGHT))
9290 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9291 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9292 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9293 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9294 Feld[x][y] = EL_AMOEBA_DROP;
9297 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9298 if (!IS_PLAYER(x,y) &&
9299 (element == EL_EMPTY ||
9300 element == EL_SAND ||
9301 element == EL_QUICKSAND_EMPTY ||
9302 element == EL_ACID_SPLASH_LEFT ||
9303 element == EL_ACID_SPLASH_RIGHT))
9305 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9306 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9307 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9308 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9309 Feld[x][y] = EL_AMOEBA_DROP;
9313 random = random * 129 + 1;
9319 if (game.explosions_delayed)
9322 game.explosions_delayed = FALSE;
9324 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9326 element = Feld[x][y];
9328 if (ExplodeField[x][y])
9329 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9330 else if (element == EL_EXPLOSION)
9331 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9333 ExplodeField[x][y] = EX_TYPE_NONE;
9336 game.explosions_delayed = TRUE;
9339 if (game.magic_wall_active)
9341 if (!(game.magic_wall_time_left % 4))
9343 int element = Feld[magic_wall_x][magic_wall_y];
9345 if (element == EL_BD_MAGIC_WALL_FULL ||
9346 element == EL_BD_MAGIC_WALL_ACTIVE ||
9347 element == EL_BD_MAGIC_WALL_EMPTYING)
9348 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9350 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9353 if (game.magic_wall_time_left > 0)
9355 game.magic_wall_time_left--;
9356 if (!game.magic_wall_time_left)
9358 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9360 element = Feld[x][y];
9362 if (element == EL_MAGIC_WALL_ACTIVE ||
9363 element == EL_MAGIC_WALL_FULL)
9365 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9366 DrawLevelField(x, y);
9368 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9369 element == EL_BD_MAGIC_WALL_FULL)
9371 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9372 DrawLevelField(x, y);
9376 game.magic_wall_active = FALSE;
9381 if (game.light_time_left > 0)
9383 game.light_time_left--;
9385 if (game.light_time_left == 0)
9386 RedrawAllLightSwitchesAndInvisibleElements();
9389 if (game.timegate_time_left > 0)
9391 game.timegate_time_left--;
9393 if (game.timegate_time_left == 0)
9394 CloseAllOpenTimegates();
9397 for (i = 0; i < MAX_PLAYERS; i++)
9399 struct PlayerInfo *player = &stored_player[i];
9401 if (SHIELD_ON(player))
9403 if (player->shield_deadly_time_left)
9404 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9405 else if (player->shield_normal_time_left)
9406 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9410 if (TimeFrames >= FRAMES_PER_SECOND)
9415 for (i = 0; i < MAX_PLAYERS; i++)
9417 struct PlayerInfo *player = &stored_player[i];
9419 if (SHIELD_ON(player))
9421 player->shield_normal_time_left--;
9423 if (player->shield_deadly_time_left > 0)
9424 player->shield_deadly_time_left--;
9428 if (!level.use_step_counter)
9436 if (TimeLeft <= 10 && setup.time_limit)
9437 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9439 DrawGameValue_Time(TimeLeft);
9441 if (!TimeLeft && setup.time_limit)
9442 for (i = 0; i < MAX_PLAYERS; i++)
9443 KillHero(&stored_player[i]);
9445 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9446 DrawGameValue_Time(TimePlayed);
9449 if (tape.recording || tape.playing)
9450 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9454 PlayAllPlayersSound();
9456 if (options.debug) /* calculate frames per second */
9458 static unsigned long fps_counter = 0;
9459 static int fps_frames = 0;
9460 unsigned long fps_delay_ms = Counter() - fps_counter;
9464 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9466 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9469 fps_counter = Counter();
9472 redraw_mask |= REDRAW_FPS;
9476 if (stored_player[0].jx != stored_player[0].last_jx ||
9477 stored_player[0].jy != stored_player[0].last_jy)
9478 printf("::: %d, %d, %d, %d, %d\n",
9479 stored_player[0].MovDir,
9480 stored_player[0].MovPos,
9481 stored_player[0].GfxPos,
9482 stored_player[0].Frame,
9483 stored_player[0].StepFrame);
9486 #if USE_NEW_MOVE_DELAY
9487 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9492 for (i = 0; i < MAX_PLAYERS; i++)
9495 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9497 stored_player[i].Frame += move_frames;
9499 if (stored_player[i].MovPos != 0)
9500 stored_player[i].StepFrame += move_frames;
9502 #if USE_NEW_MOVE_DELAY
9503 if (stored_player[i].move_delay > 0)
9504 stored_player[i].move_delay--;
9507 if (stored_player[i].drop_delay > 0)
9508 stored_player[i].drop_delay--;
9513 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9515 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9517 local_player->show_envelope = 0;
9521 #if USE_NEW_RANDOMIZE
9522 /* use random number generator in every frame to make it less predictable */
9523 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9528 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9530 int min_x = x, min_y = y, max_x = x, max_y = y;
9533 for (i = 0; i < MAX_PLAYERS; i++)
9535 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9537 if (!stored_player[i].active || &stored_player[i] == player)
9540 min_x = MIN(min_x, jx);
9541 min_y = MIN(min_y, jy);
9542 max_x = MAX(max_x, jx);
9543 max_y = MAX(max_y, jy);
9546 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9549 static boolean AllPlayersInVisibleScreen()
9553 for (i = 0; i < MAX_PLAYERS; i++)
9555 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9557 if (!stored_player[i].active)
9560 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9567 void ScrollLevel(int dx, int dy)
9569 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9572 BlitBitmap(drawto_field, drawto_field,
9573 FX + TILEX * (dx == -1) - softscroll_offset,
9574 FY + TILEY * (dy == -1) - softscroll_offset,
9575 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9576 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9577 FX + TILEX * (dx == 1) - softscroll_offset,
9578 FY + TILEY * (dy == 1) - softscroll_offset);
9582 x = (dx == 1 ? BX1 : BX2);
9583 for (y = BY1; y <= BY2; y++)
9584 DrawScreenField(x, y);
9589 y = (dy == 1 ? BY1 : BY2);
9590 for (x = BX1; x <= BX2; x++)
9591 DrawScreenField(x, y);
9594 redraw_mask |= REDRAW_FIELD;
9598 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9600 int nextx = x + dx, nexty = y + dy;
9601 int element = Feld[x][y];
9604 element != EL_SP_PORT_LEFT &&
9605 element != EL_SP_GRAVITY_PORT_LEFT &&
9606 element != EL_SP_PORT_HORIZONTAL &&
9607 element != EL_SP_PORT_ANY) ||
9609 element != EL_SP_PORT_RIGHT &&
9610 element != EL_SP_GRAVITY_PORT_RIGHT &&
9611 element != EL_SP_PORT_HORIZONTAL &&
9612 element != EL_SP_PORT_ANY) ||
9614 element != EL_SP_PORT_UP &&
9615 element != EL_SP_GRAVITY_PORT_UP &&
9616 element != EL_SP_PORT_VERTICAL &&
9617 element != EL_SP_PORT_ANY) ||
9619 element != EL_SP_PORT_DOWN &&
9620 element != EL_SP_GRAVITY_PORT_DOWN &&
9621 element != EL_SP_PORT_VERTICAL &&
9622 element != EL_SP_PORT_ANY) ||
9623 !IN_LEV_FIELD(nextx, nexty) ||
9624 !IS_FREE(nextx, nexty))
9631 static boolean canFallDown(struct PlayerInfo *player)
9633 int jx = player->jx, jy = player->jy;
9635 return (IN_LEV_FIELD(jx, jy + 1) &&
9636 (IS_FREE(jx, jy + 1) ||
9637 #if USE_NEW_BLOCK_STYLE
9638 #if USE_GRAVITY_BUGFIX_OLD
9639 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9642 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9643 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9644 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9647 static boolean canPassField(int x, int y, int move_dir)
9649 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9650 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9651 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9654 int element = Feld[x][y];
9656 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9657 !CAN_MOVE(element) &&
9658 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9659 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9660 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9663 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9665 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9666 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9667 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9671 int nextx = newx + dx;
9672 int nexty = newy + dy;
9676 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9677 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9679 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9681 (IS_DIGGABLE(Feld[newx][newy]) ||
9682 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9683 canPassField(newx, newy, move_dir)));
9686 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9687 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9688 (IS_DIGGABLE(Feld[newx][newy]) ||
9689 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9690 canPassField(newx, newy, move_dir)));
9693 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9694 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9695 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9696 canPassField(newx, newy, move_dir)));
9698 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9699 (IS_DIGGABLE(Feld[newx][newy]) ||
9700 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9701 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9702 !CAN_MOVE(Feld[newx][newy]) &&
9703 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9704 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9705 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9711 static void CheckGravityMovement(struct PlayerInfo *player)
9713 if (game.gravity && !player->programmed_action)
9716 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9717 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9719 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9720 int move_dir_vertical = player->action & MV_VERTICAL;
9724 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9726 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9729 int jx = player->jx, jy = player->jy;
9731 boolean player_is_moving_to_valid_field =
9732 (!player_is_snapping &&
9733 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9734 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9738 (player->last_move_dir & MV_HORIZONTAL ?
9739 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9740 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9744 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9745 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9746 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9747 int new_jx = jx + dx, new_jy = jy + dy;
9748 int nextx = new_jx + dx, nexty = new_jy + dy;
9754 boolean player_can_fall_down = canFallDown(player);
9756 boolean player_can_fall_down =
9757 (IN_LEV_FIELD(jx, jy + 1) &&
9758 (IS_FREE(jx, jy + 1) ||
9759 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9763 boolean player_can_fall_down =
9764 (IN_LEV_FIELD(jx, jy + 1) &&
9765 (IS_FREE(jx, jy + 1)));
9769 boolean player_is_moving_to_valid_field =
9772 !player_is_snapping &&
9776 IN_LEV_FIELD(new_jx, new_jy) &&
9777 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9778 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9779 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9780 IN_LEV_FIELD(nextx, nexty) &&
9781 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9783 IN_LEV_FIELD(new_jx, new_jy) &&
9784 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9785 Feld[new_jx][new_jy] == EL_SAND ||
9786 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9787 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9788 /* !!! extend EL_SAND to anything diggable !!! */
9794 boolean player_is_standing_on_valid_field =
9795 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9796 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9800 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9801 player_can_fall_down,
9802 player_is_standing_on_valid_field,
9803 player_is_moving_to_valid_field,
9804 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9805 player->effective_action,
9806 player->can_fall_into_acid);
9809 if (player_can_fall_down &&
9811 !player_is_standing_on_valid_field &&
9813 !player_is_moving_to_valid_field)
9816 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9817 jx, jy, FrameCounter);
9820 player->programmed_action = MV_DOWN;
9825 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9828 return CheckGravityMovement(player);
9831 if (game.gravity && !player->programmed_action)
9833 int jx = player->jx, jy = player->jy;
9834 boolean field_under_player_is_free =
9835 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9836 boolean player_is_standing_on_valid_field =
9837 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9838 (IS_WALKABLE(Feld[jx][jy]) &&
9839 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9841 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9842 player->programmed_action = MV_DOWN;
9848 -----------------------------------------------------------------------------
9849 dx, dy: direction (non-diagonal) to try to move the player to
9850 real_dx, real_dy: direction as read from input device (can be diagonal)
9853 boolean MovePlayerOneStep(struct PlayerInfo *player,
9854 int dx, int dy, int real_dx, int real_dy)
9857 static int trigger_sides[4][2] =
9859 /* enter side leave side */
9860 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9861 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9862 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9863 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9865 int move_direction = (dx == -1 ? MV_LEFT :
9866 dx == +1 ? MV_RIGHT :
9868 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9869 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9870 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9872 int jx = player->jx, jy = player->jy;
9873 int new_jx = jx + dx, new_jy = jy + dy;
9877 if (!player->active || (!dx && !dy))
9878 return MF_NO_ACTION;
9880 player->MovDir = (dx < 0 ? MV_LEFT :
9883 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9885 if (!IN_LEV_FIELD(new_jx, new_jy))
9886 return MF_NO_ACTION;
9888 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9889 return MF_NO_ACTION;
9892 element = MovingOrBlocked2Element(new_jx, new_jy);
9894 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9897 if (DONT_RUN_INTO(element))
9899 if (element == EL_ACID && dx == 0 && dy == 1)
9901 SplashAcid(new_jx, new_jy);
9902 Feld[jx][jy] = EL_PLAYER_1;
9903 InitMovingField(jx, jy, MV_DOWN);
9904 Store[jx][jy] = EL_ACID;
9905 ContinueMoving(jx, jy);
9909 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9914 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9915 if (can_move != MF_MOVING)
9918 /* check if DigField() has caused relocation of the player */
9919 if (player->jx != jx || player->jy != jy)
9920 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9922 StorePlayer[jx][jy] = 0;
9923 player->last_jx = jx;
9924 player->last_jy = jy;
9925 player->jx = new_jx;
9926 player->jy = new_jy;
9927 StorePlayer[new_jx][new_jy] = player->element_nr;
9930 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9932 player->step_counter++;
9935 player->drop_delay = 0;
9938 PlayerVisit[jx][jy] = FrameCounter;
9940 ScrollPlayer(player, SCROLL_INIT);
9943 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9945 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
9947 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9950 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9952 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9953 CE_PLAYER_ENTERS_X, enter_side);
9954 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9955 CE_ENTERED_BY_PLAYER, enter_side);
9962 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9964 int jx = player->jx, jy = player->jy;
9965 int old_jx = jx, old_jy = jy;
9966 int moved = MF_NO_ACTION;
9969 if (!player->active)
9974 if (player->MovPos == 0)
9976 player->is_moving = FALSE;
9977 player->is_digging = FALSE;
9978 player->is_collecting = FALSE;
9979 player->is_snapping = FALSE;
9980 player->is_pushing = FALSE;
9986 if (!player->active || (!dx && !dy))
9991 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9999 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
10000 player->move_delay + player->move_delay_value);
10003 #if USE_NEW_MOVE_DELAY
10004 if (player->move_delay > 0)
10006 if (!FrameReached(&player->move_delay, player->move_delay_value))
10010 printf("::: can NOT move\n");
10016 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10017 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10024 printf("::: COULD move now\n");
10027 #if USE_NEW_MOVE_DELAY
10028 player->move_delay = -1; /* set to "uninitialized" value */
10031 /* store if player is automatically moved to next field */
10032 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10034 /* remove the last programmed player action */
10035 player->programmed_action = 0;
10037 if (player->MovPos)
10039 /* should only happen if pre-1.2 tape recordings are played */
10040 /* this is only for backward compatibility */
10042 int original_move_delay_value = player->move_delay_value;
10045 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10049 /* scroll remaining steps with finest movement resolution */
10050 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10052 while (player->MovPos)
10054 ScrollPlayer(player, SCROLL_GO_ON);
10055 ScrollScreen(NULL, SCROLL_GO_ON);
10057 #if USE_NEW_MOVE_DELAY
10058 AdvanceFrameAndPlayerCounters(player->index_nr);
10067 player->move_delay_value = original_move_delay_value;
10070 if (player->last_move_dir & MV_HORIZONTAL)
10072 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10073 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10077 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10078 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10084 if (moved & MF_MOVING && !ScreenMovPos &&
10085 (player == local_player || !options.network))
10087 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10088 int offset = (setup.scroll_delay ? 3 : 0);
10090 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10092 /* actual player has left the screen -- scroll in that direction */
10093 if (jx != old_jx) /* player has moved horizontally */
10094 scroll_x += (jx - old_jx);
10095 else /* player has moved vertically */
10096 scroll_y += (jy - old_jy);
10100 if (jx != old_jx) /* player has moved horizontally */
10102 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10103 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10104 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10106 /* don't scroll over playfield boundaries */
10107 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10108 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10110 /* don't scroll more than one field at a time */
10111 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10113 /* don't scroll against the player's moving direction */
10114 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10115 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10116 scroll_x = old_scroll_x;
10118 else /* player has moved vertically */
10120 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10121 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10122 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10124 /* don't scroll over playfield boundaries */
10125 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10126 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10128 /* don't scroll more than one field at a time */
10129 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10131 /* don't scroll against the player's moving direction */
10132 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10133 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10134 scroll_y = old_scroll_y;
10138 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10140 if (!options.network && !AllPlayersInVisibleScreen())
10142 scroll_x = old_scroll_x;
10143 scroll_y = old_scroll_y;
10147 ScrollScreen(player, SCROLL_INIT);
10148 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10155 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10157 if (!(moved & MF_MOVING) && !player->is_pushing)
10162 player->StepFrame = 0;
10164 if (moved & MF_MOVING)
10167 printf("::: REALLY moves now\n");
10170 if (old_jx != jx && old_jy == jy)
10171 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10172 else if (old_jx == jx && old_jy != jy)
10173 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10175 DrawLevelField(jx, jy); /* for "crumbled sand" */
10177 player->last_move_dir = player->MovDir;
10178 player->is_moving = TRUE;
10180 player->is_snapping = FALSE;
10184 player->is_switching = FALSE;
10187 player->is_dropping = FALSE;
10191 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10194 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10197 int move_direction = player->MovDir;
10199 int enter_side = MV_DIR_OPPOSITE(move_direction);
10200 int leave_side = move_direction;
10202 static int trigger_sides[4][2] =
10204 /* enter side leave side */
10205 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10206 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10207 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10208 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10210 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10211 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10213 int old_element = Feld[old_jx][old_jy];
10214 int new_element = Feld[jx][jy];
10217 /* !!! TEST ONLY !!! */
10218 if (IS_CUSTOM_ELEMENT(old_element))
10219 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10221 player->index_bit, leave_side);
10223 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10224 CE_PLAYER_LEAVES_X,
10225 player->index_bit, leave_side);
10227 if (IS_CUSTOM_ELEMENT(new_element))
10228 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10229 player->index_bit, enter_side);
10231 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10232 CE_PLAYER_ENTERS_X,
10233 player->index_bit, enter_side);
10243 CheckGravityMovementWhenNotMoving(player);
10246 player->last_move_dir = MV_NO_MOVING;
10248 player->is_moving = FALSE;
10250 #if USE_NEW_MOVE_STYLE
10251 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10252 /* ensure that the player is also allowed to move in the next frame */
10253 /* (currently, the player is forced to wait eight frames before he can try
10256 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10257 player->move_delay = 0; /* allow direct movement in the next frame */
10261 #if USE_NEW_MOVE_DELAY
10262 if (player->move_delay == -1) /* not yet initialized by DigField() */
10263 player->move_delay = player->move_delay_value;
10266 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10268 TestIfHeroTouchesBadThing(jx, jy);
10269 TestIfPlayerTouchesCustomElement(jx, jy);
10272 if (!player->active)
10273 RemoveHero(player);
10278 void ScrollPlayer(struct PlayerInfo *player, int mode)
10280 int jx = player->jx, jy = player->jy;
10281 int last_jx = player->last_jx, last_jy = player->last_jy;
10282 int move_stepsize = TILEX / player->move_delay_value;
10284 if (!player->active || !player->MovPos)
10287 if (mode == SCROLL_INIT)
10289 player->actual_frame_counter = FrameCounter;
10290 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10293 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10295 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10296 player->block_delay);
10299 #if USE_NEW_BLOCK_STYLE
10302 if (player->block_delay <= 0)
10303 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10306 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10307 Feld[last_jx][last_jy] == EL_EMPTY)
10309 int last_field_block_delay = 0; /* start with no blocking at all */
10310 int block_delay_adjustment = player->block_delay_adjustment;
10312 /* if player blocks last field, add delay for exactly one move */
10313 if (player->block_last_field)
10315 last_field_block_delay += player->move_delay_value;
10317 #if USE_GRAVITY_BUGFIX_NEW
10318 /* when blocking enabled, prevent moving up despite gravity */
10319 if (game.gravity && player->MovDir == MV_UP)
10320 block_delay_adjustment = -1;
10324 /* add block delay adjustment (also possible when not blocking) */
10325 last_field_block_delay += block_delay_adjustment;
10328 #if USE_BLOCK_DELAY_BUGFIX
10329 /* when blocking enabled, correct block delay for fast movement */
10330 if (player->block_last_field &&
10331 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10332 last_field_block_delay =
10333 player->move_delay_value + player->block_delay_adjustment;
10338 #if USE_GRAVITY_BUGFIX_NEW
10339 /* when blocking enabled, correct block delay for gravity movement */
10340 if (player->block_last_field &&
10341 game.gravity && player->MovDir == MV_UP)
10342 last_field_block_delay = player->move_delay_value - 1;
10346 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10347 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10350 #if USE_NEW_MOVE_STYLE
10351 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10352 player->block_last_field) &&
10353 Feld[last_jx][last_jy] == EL_EMPTY)
10354 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10356 if (Feld[last_jx][last_jy] == EL_EMPTY)
10357 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10362 DrawPlayer(player);
10367 else if (!FrameReached(&player->actual_frame_counter, 1))
10370 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10371 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10373 #if USE_NEW_BLOCK_STYLE
10375 if (!player->block_last_field &&
10376 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10378 RemoveField(last_jx, last_jy);
10380 Feld[last_jx][last_jy] = EL_EMPTY;
10384 /* before DrawPlayer() to draw correct player graphic for this case */
10385 if (player->MovPos == 0)
10386 CheckGravityMovement(player);
10389 DrawPlayer(player); /* needed here only to cleanup last field */
10392 if (player->MovPos == 0) /* player reached destination field */
10395 if (player->move_delay_reset_counter > 0)
10397 player->move_delay_reset_counter--;
10399 if (player->move_delay_reset_counter == 0)
10401 /* continue with normal speed after quickly moving through gate */
10402 HALVE_PLAYER_SPEED(player);
10404 /* be able to make the next move without delay */
10405 player->move_delay = 0;
10409 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10411 /* continue with normal speed after quickly moving through gate */
10412 HALVE_PLAYER_SPEED(player);
10414 /* be able to make the next move without delay */
10415 player->move_delay = 0;
10419 #if USE_NEW_BLOCK_STYLE
10421 if (player->block_last_field &&
10422 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10424 RemoveField(last_jx, last_jy);
10426 Feld[last_jx][last_jy] = EL_EMPTY;
10430 player->last_jx = jx;
10431 player->last_jy = jy;
10433 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10434 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10435 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10437 DrawPlayer(player); /* needed here only to cleanup last field */
10438 RemoveHero(player);
10440 if (local_player->friends_still_needed == 0 ||
10441 IS_SP_ELEMENT(Feld[jx][jy]))
10442 player->LevelSolved = player->GameOver = TRUE;
10446 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10447 /* this breaks one level: "machine", level 000 */
10449 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10452 int move_direction = player->MovDir;
10454 int enter_side = MV_DIR_OPPOSITE(move_direction);
10455 int leave_side = move_direction;
10457 static int trigger_sides[4][2] =
10459 /* enter side leave side */
10460 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10461 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10462 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10463 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10465 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10466 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10468 int old_jx = last_jx;
10469 int old_jy = last_jy;
10470 int old_element = Feld[old_jx][old_jy];
10471 int new_element = Feld[jx][jy];
10474 /* !!! TEST ONLY !!! */
10475 if (IS_CUSTOM_ELEMENT(old_element))
10476 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10478 player->index_bit, leave_side);
10480 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10481 CE_PLAYER_LEAVES_X,
10482 player->index_bit, leave_side);
10484 if (IS_CUSTOM_ELEMENT(new_element))
10485 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10486 player->index_bit, enter_side);
10488 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10489 CE_PLAYER_ENTERS_X,
10490 player->index_bit, enter_side);
10496 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10498 TestIfHeroTouchesBadThing(jx, jy);
10499 TestIfPlayerTouchesCustomElement(jx, jy);
10502 /* needed because pushed element has not yet reached its destination,
10503 so it would trigger a change event at its previous field location */
10504 if (!player->is_pushing)
10506 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10509 if (!player->active)
10510 RemoveHero(player);
10513 if (level.use_step_counter)
10523 if (TimeLeft <= 10 && setup.time_limit)
10524 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10526 DrawGameValue_Time(TimeLeft);
10528 if (!TimeLeft && setup.time_limit)
10529 for (i = 0; i < MAX_PLAYERS; i++)
10530 KillHero(&stored_player[i]);
10532 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10533 DrawGameValue_Time(TimePlayed);
10536 if (tape.single_step && tape.recording && !tape.pausing &&
10537 !player->programmed_action)
10538 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10542 void ScrollScreen(struct PlayerInfo *player, int mode)
10544 static unsigned long screen_frame_counter = 0;
10546 if (mode == SCROLL_INIT)
10548 /* set scrolling step size according to actual player's moving speed */
10549 ScrollStepSize = TILEX / player->move_delay_value;
10551 screen_frame_counter = FrameCounter;
10552 ScreenMovDir = player->MovDir;
10553 ScreenMovPos = player->MovPos;
10554 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10557 else if (!FrameReached(&screen_frame_counter, 1))
10562 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10563 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10564 redraw_mask |= REDRAW_FIELD;
10567 ScreenMovDir = MV_NO_MOVING;
10570 void TestIfPlayerTouchesCustomElement(int x, int y)
10572 static int xy[4][2] =
10579 static int trigger_sides[4][2] =
10581 /* center side border side */
10582 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10583 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10584 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10585 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10587 static int touch_dir[4] =
10589 MV_LEFT | MV_RIGHT,
10594 int center_element = Feld[x][y]; /* should always be non-moving! */
10597 for (i = 0; i < NUM_DIRECTIONS; i++)
10599 int xx = x + xy[i][0];
10600 int yy = y + xy[i][1];
10601 int center_side = trigger_sides[i][0];
10602 int border_side = trigger_sides[i][1];
10603 int border_element;
10605 if (!IN_LEV_FIELD(xx, yy))
10608 if (IS_PLAYER(x, y))
10610 struct PlayerInfo *player = PLAYERINFO(x, y);
10612 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10613 border_element = Feld[xx][yy]; /* may be moving! */
10614 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10615 border_element = Feld[xx][yy];
10616 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10617 border_element = MovingOrBlocked2Element(xx, yy);
10619 continue; /* center and border element do not touch */
10622 /* !!! TEST ONLY !!! */
10623 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10624 player->index_bit, border_side);
10625 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10626 CE_PLAYER_TOUCHES_X,
10627 player->index_bit, border_side);
10629 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10630 CE_PLAYER_TOUCHES_X,
10631 player->index_bit, border_side);
10632 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10633 player->index_bit, border_side);
10636 else if (IS_PLAYER(xx, yy))
10638 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10640 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10642 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10643 continue; /* center and border element do not touch */
10647 /* !!! TEST ONLY !!! */
10648 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10649 player->index_bit, center_side);
10650 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10651 CE_PLAYER_TOUCHES_X,
10652 player->index_bit, center_side);
10654 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10655 CE_PLAYER_TOUCHES_X,
10656 player->index_bit, center_side);
10657 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10658 player->index_bit, center_side);
10666 void TestIfElementTouchesCustomElement(int x, int y)
10668 static int xy[4][2] =
10675 static int trigger_sides[4][2] =
10677 /* center side border side */
10678 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10679 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10680 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10681 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10683 static int touch_dir[4] =
10685 MV_LEFT | MV_RIGHT,
10690 boolean change_center_element = FALSE;
10691 int center_element_change_page = 0;
10692 int center_element = Feld[x][y]; /* should always be non-moving! */
10693 int border_trigger_element = EL_UNDEFINED;
10696 for (i = 0; i < NUM_DIRECTIONS; i++)
10698 int xx = x + xy[i][0];
10699 int yy = y + xy[i][1];
10700 int center_side = trigger_sides[i][0];
10701 int border_side = trigger_sides[i][1];
10702 int border_element;
10704 if (!IN_LEV_FIELD(xx, yy))
10707 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10708 border_element = Feld[xx][yy]; /* may be moving! */
10709 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10710 border_element = Feld[xx][yy];
10711 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10712 border_element = MovingOrBlocked2Element(xx, yy);
10714 continue; /* center and border element do not touch */
10716 /* check for change of center element (but change it only once) */
10717 if (IS_CUSTOM_ELEMENT(center_element) &&
10718 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
10719 !change_center_element)
10721 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10723 struct ElementChangeInfo *change =
10724 &element_info[center_element].change_page[j];
10726 if (change->can_change &&
10727 change->has_event[CE_TOUCHING_X] &&
10728 change->trigger_side & border_side &&
10730 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10732 change->trigger_element == border_element
10736 change_center_element = TRUE;
10737 center_element_change_page = j;
10738 border_trigger_element = border_element;
10745 /* check for change of border element */
10746 if (IS_CUSTOM_ELEMENT(border_element) &&
10747 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
10749 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10751 struct ElementChangeInfo *change =
10752 &element_info[border_element].change_page[j];
10754 if (change->can_change &&
10755 change->has_event[CE_TOUCHING_X] &&
10756 change->trigger_side & center_side &&
10758 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10760 change->trigger_element == center_element
10765 printf("::: border_element %d, %d\n", x, y);
10768 CheckElementChangeByPage(xx, yy, border_element, center_element,
10776 if (change_center_element)
10779 printf("::: center_element %d, %d\n", x, y);
10782 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10783 CE_TOUCHING_X, center_element_change_page);
10787 void TestIfElementHitsCustomElement(int x, int y, int direction)
10789 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10790 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10791 int hitx = x + dx, hity = y + dy;
10792 int hitting_element = Feld[x][y];
10793 int touched_element;
10795 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10796 !IS_FREE(hitx, hity) &&
10797 (!IS_MOVING(hitx, hity) ||
10798 MovDir[hitx][hity] != direction ||
10799 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10802 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10806 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10810 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10811 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10813 #if !USE_HITTING_SOMETHING_BUGFIX
10814 /* "hitting something" is also true when hitting the playfield border */
10815 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10816 CE_HITTING_SOMETHING, direction);
10819 if (IN_LEV_FIELD(hitx, hity))
10821 int opposite_direction = MV_DIR_OPPOSITE(direction);
10822 int hitting_side = direction;
10823 int touched_side = opposite_direction;
10825 int touched_element = MovingOrBlocked2Element(hitx, hity);
10828 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10829 MovDir[hitx][hity] != direction ||
10830 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10839 #if !USE_HIT_BY_SOMETHING_BUGFIX
10840 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10841 CE_HIT_BY_SOMETHING, opposite_direction);
10844 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10845 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
10847 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10849 struct ElementChangeInfo *change =
10850 &element_info[hitting_element].change_page[i];
10852 if (change->can_change &&
10853 change->has_event[CE_HITTING_X] &&
10854 change->trigger_side & touched_side &&
10857 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10859 change->trigger_element == touched_element
10863 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10870 if (IS_CUSTOM_ELEMENT(touched_element) &&
10871 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
10873 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10875 struct ElementChangeInfo *change =
10876 &element_info[touched_element].change_page[i];
10878 if (change->can_change &&
10879 change->has_event[CE_HIT_BY_X] &&
10880 change->trigger_side & hitting_side &&
10882 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10884 change->trigger_element == hitting_element
10888 CheckElementChangeByPage(hitx, hity, touched_element,
10889 hitting_element, CE_HIT_BY_X, i);
10895 #if USE_HIT_BY_SOMETHING_BUGFIX
10896 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10897 CE_HIT_BY_SOMETHING, opposite_direction);
10902 #if USE_HITTING_SOMETHING_BUGFIX
10903 /* "hitting something" is also true when hitting the playfield border */
10904 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10905 CE_HITTING_SOMETHING, direction);
10910 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10912 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10913 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10914 int hitx = x + dx, hity = y + dy;
10915 int hitting_element = Feld[x][y];
10916 int touched_element;
10918 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10919 !IS_FREE(hitx, hity) &&
10920 (!IS_MOVING(hitx, hity) ||
10921 MovDir[hitx][hity] != direction ||
10922 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10925 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10929 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10933 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10934 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10936 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10937 EP_CAN_SMASH_EVERYTHING, direction);
10939 if (IN_LEV_FIELD(hitx, hity))
10941 int opposite_direction = MV_DIR_OPPOSITE(direction);
10942 int hitting_side = direction;
10943 int touched_side = opposite_direction;
10945 int touched_element = MovingOrBlocked2Element(hitx, hity);
10948 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10949 MovDir[hitx][hity] != direction ||
10950 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10959 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10960 CE_SMASHED_BY_SOMETHING, opposite_direction);
10962 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10963 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10965 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10967 struct ElementChangeInfo *change =
10968 &element_info[hitting_element].change_page[i];
10970 if (change->can_change &&
10971 change->has_event[CE_OTHER_IS_SMASHING] &&
10972 change->trigger_side & touched_side &&
10975 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10977 change->trigger_element == touched_element
10981 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10982 CE_OTHER_IS_SMASHING, i);
10988 if (IS_CUSTOM_ELEMENT(touched_element) &&
10989 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10991 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10993 struct ElementChangeInfo *change =
10994 &element_info[touched_element].change_page[i];
10996 if (change->can_change &&
10997 change->has_event[CE_OTHER_GETS_SMASHED] &&
10998 change->trigger_side & hitting_side &&
11000 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11002 change->trigger_element == hitting_element
11006 CheckElementChangeByPage(hitx, hity, touched_element,
11007 hitting_element, CE_OTHER_GETS_SMASHED,i);
11017 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11019 int i, kill_x = -1, kill_y = -1;
11020 int bad_element = -1;
11021 static int test_xy[4][2] =
11028 static int test_dir[4] =
11036 for (i = 0; i < NUM_DIRECTIONS; i++)
11038 int test_x, test_y, test_move_dir, test_element;
11040 test_x = good_x + test_xy[i][0];
11041 test_y = good_y + test_xy[i][1];
11043 if (!IN_LEV_FIELD(test_x, test_y))
11047 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11050 test_element = Feld[test_x][test_y];
11052 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11055 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11056 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11058 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11059 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11063 bad_element = test_element;
11069 if (kill_x != -1 || kill_y != -1)
11071 if (IS_PLAYER(good_x, good_y))
11073 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11076 if (player->shield_deadly_time_left > 0 &&
11077 !IS_INDESTRUCTIBLE(bad_element))
11078 Bang(kill_x, kill_y);
11079 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11082 if (player->shield_deadly_time_left > 0)
11083 Bang(kill_x, kill_y);
11084 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11089 Bang(good_x, good_y);
11093 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11095 int i, kill_x = -1, kill_y = -1;
11096 int bad_element = Feld[bad_x][bad_y];
11097 static int test_xy[4][2] =
11104 static int touch_dir[4] =
11106 MV_LEFT | MV_RIGHT,
11111 static int test_dir[4] =
11119 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11122 for (i = 0; i < NUM_DIRECTIONS; i++)
11124 int test_x, test_y, test_move_dir, test_element;
11126 test_x = bad_x + test_xy[i][0];
11127 test_y = bad_y + test_xy[i][1];
11128 if (!IN_LEV_FIELD(test_x, test_y))
11132 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11134 test_element = Feld[test_x][test_y];
11136 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11137 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11139 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11140 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11142 /* good thing is player or penguin that does not move away */
11143 if (IS_PLAYER(test_x, test_y))
11145 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11147 if (bad_element == EL_ROBOT && player->is_moving)
11148 continue; /* robot does not kill player if he is moving */
11150 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11152 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11153 continue; /* center and border element do not touch */
11160 else if (test_element == EL_PENGUIN)
11169 if (kill_x != -1 || kill_y != -1)
11171 if (IS_PLAYER(kill_x, kill_y))
11173 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11176 if (player->shield_deadly_time_left > 0 &&
11177 !IS_INDESTRUCTIBLE(bad_element))
11178 Bang(bad_x, bad_y);
11179 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11182 if (player->shield_deadly_time_left > 0)
11183 Bang(bad_x, bad_y);
11184 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11189 Bang(kill_x, kill_y);
11193 void TestIfHeroTouchesBadThing(int x, int y)
11195 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11198 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11200 TestIfGoodThingHitsBadThing(x, y, move_dir);
11203 void TestIfBadThingTouchesHero(int x, int y)
11205 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11208 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11210 TestIfBadThingHitsGoodThing(x, y, move_dir);
11213 void TestIfFriendTouchesBadThing(int x, int y)
11215 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11218 void TestIfBadThingTouchesFriend(int x, int y)
11220 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11223 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11225 int i, kill_x = bad_x, kill_y = bad_y;
11226 static int xy[4][2] =
11234 for (i = 0; i < NUM_DIRECTIONS; i++)
11238 x = bad_x + xy[i][0];
11239 y = bad_y + xy[i][1];
11240 if (!IN_LEV_FIELD(x, y))
11243 element = Feld[x][y];
11244 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11245 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11253 if (kill_x != bad_x || kill_y != bad_y)
11254 Bang(bad_x, bad_y);
11257 void KillHero(struct PlayerInfo *player)
11259 int jx = player->jx, jy = player->jy;
11261 if (!player->active)
11264 /* remove accessible field at the player's position */
11265 Feld[jx][jy] = EL_EMPTY;
11267 /* deactivate shield (else Bang()/Explode() would not work right) */
11268 player->shield_normal_time_left = 0;
11269 player->shield_deadly_time_left = 0;
11275 static void KillHeroUnlessEnemyProtected(int x, int y)
11277 if (!PLAYER_ENEMY_PROTECTED(x, y))
11278 KillHero(PLAYERINFO(x, y));
11281 static void KillHeroUnlessExplosionProtected(int x, int y)
11283 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11284 KillHero(PLAYERINFO(x, y));
11287 void BuryHero(struct PlayerInfo *player)
11289 int jx = player->jx, jy = player->jy;
11291 if (!player->active)
11295 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11297 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11299 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11301 player->GameOver = TRUE;
11302 RemoveHero(player);
11305 void RemoveHero(struct PlayerInfo *player)
11307 int jx = player->jx, jy = player->jy;
11308 int i, found = FALSE;
11310 player->present = FALSE;
11311 player->active = FALSE;
11313 if (!ExplodeField[jx][jy])
11314 StorePlayer[jx][jy] = 0;
11316 for (i = 0; i < MAX_PLAYERS; i++)
11317 if (stored_player[i].active)
11321 AllPlayersGone = TRUE;
11328 =============================================================================
11329 checkDiagonalPushing()
11330 -----------------------------------------------------------------------------
11331 check if diagonal input device direction results in pushing of object
11332 (by checking if the alternative direction is walkable, diggable, ...)
11333 =============================================================================
11336 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11337 int x, int y, int real_dx, int real_dy)
11339 int jx, jy, dx, dy, xx, yy;
11341 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11344 /* diagonal direction: check alternative direction */
11349 xx = jx + (dx == 0 ? real_dx : 0);
11350 yy = jy + (dy == 0 ? real_dy : 0);
11352 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11356 =============================================================================
11358 -----------------------------------------------------------------------------
11359 x, y: field next to player (non-diagonal) to try to dig to
11360 real_dx, real_dy: direction as read from input device (can be diagonal)
11361 =============================================================================
11364 int DigField(struct PlayerInfo *player,
11365 int oldx, int oldy, int x, int y,
11366 int real_dx, int real_dy, int mode)
11369 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11371 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11372 boolean player_was_pushing = player->is_pushing;
11373 int jx = oldx, jy = oldy;
11374 int dx = x - jx, dy = y - jy;
11375 int nextx = x + dx, nexty = y + dy;
11376 int move_direction = (dx == -1 ? MV_LEFT :
11377 dx == +1 ? MV_RIGHT :
11379 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11380 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11382 int dig_side = MV_DIR_OPPOSITE(move_direction);
11384 static int trigger_sides[4] =
11386 CH_SIDE_RIGHT, /* moving left */
11387 CH_SIDE_LEFT, /* moving right */
11388 CH_SIDE_BOTTOM, /* moving up */
11389 CH_SIDE_TOP, /* moving down */
11391 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11393 int old_element = Feld[jx][jy];
11396 if (is_player) /* function can also be called by EL_PENGUIN */
11398 if (player->MovPos == 0)
11400 player->is_digging = FALSE;
11401 player->is_collecting = FALSE;
11404 if (player->MovPos == 0) /* last pushing move finished */
11405 player->is_pushing = FALSE;
11407 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11409 player->is_switching = FALSE;
11410 #if USE_NEW_PUSH_DELAY
11411 player->push_delay = -1;
11413 player->push_delay = 0;
11416 return MF_NO_ACTION;
11420 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11421 return MF_NO_ACTION;
11426 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11428 if (IS_TUBE(Feld[jx][jy]) ||
11429 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11433 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11434 int tube_leave_directions[][2] =
11436 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11437 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11438 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11439 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11440 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11441 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11442 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11443 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11444 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11445 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11446 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11447 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11450 while (tube_leave_directions[i][0] != tube_element)
11453 if (tube_leave_directions[i][0] == -1) /* should not happen */
11457 if (!(tube_leave_directions[i][1] & move_direction))
11458 return MF_NO_ACTION; /* tube has no opening in this direction */
11463 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11464 old_element = Back[jx][jy];
11465 #if USE_BACK_WALKABLE_BUGFIX
11467 /* in case of element dropped at player position, check background */
11468 else if (Back[jx][jy] != EL_EMPTY &&
11469 game.engine_version >= VERSION_IDENT(2,2,0,0))
11470 old_element = Back[jx][jy];
11475 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11476 return MF_NO_ACTION; /* field has no opening in this direction */
11478 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11479 return MF_NO_ACTION; /* field has no opening in this direction */
11481 element = Feld[x][y];
11483 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11484 return MF_NO_ACTION;
11486 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11487 game.engine_version >= VERSION_IDENT(2,2,0,0))
11488 return MF_NO_ACTION;
11491 if (game.gravity && is_player && !player->is_auto_moving &&
11492 canFallDown(player) && move_direction != MV_DOWN &&
11493 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11494 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11498 if (element == EL_EMPTY_SPACE &&
11499 game.gravity && !player->is_auto_moving &&
11500 canFallDown(player) && move_direction != MV_DOWN)
11501 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11507 case EL_SP_PORT_LEFT:
11508 case EL_SP_PORT_RIGHT:
11509 case EL_SP_PORT_UP:
11510 case EL_SP_PORT_DOWN:
11511 case EL_SP_PORT_HORIZONTAL:
11512 case EL_SP_PORT_VERTICAL:
11513 case EL_SP_PORT_ANY:
11514 case EL_SP_GRAVITY_PORT_LEFT:
11515 case EL_SP_GRAVITY_PORT_RIGHT:
11516 case EL_SP_GRAVITY_PORT_UP:
11517 case EL_SP_GRAVITY_PORT_DOWN:
11519 if (!canEnterSupaplexPort(x, y, dx, dy))
11520 return MF_NO_ACTION;
11523 element != EL_SP_PORT_LEFT &&
11524 element != EL_SP_GRAVITY_PORT_LEFT &&
11525 element != EL_SP_PORT_HORIZONTAL &&
11526 element != EL_SP_PORT_ANY) ||
11528 element != EL_SP_PORT_RIGHT &&
11529 element != EL_SP_GRAVITY_PORT_RIGHT &&
11530 element != EL_SP_PORT_HORIZONTAL &&
11531 element != EL_SP_PORT_ANY) ||
11533 element != EL_SP_PORT_UP &&
11534 element != EL_SP_GRAVITY_PORT_UP &&
11535 element != EL_SP_PORT_VERTICAL &&
11536 element != EL_SP_PORT_ANY) ||
11538 element != EL_SP_PORT_DOWN &&
11539 element != EL_SP_GRAVITY_PORT_DOWN &&
11540 element != EL_SP_PORT_VERTICAL &&
11541 element != EL_SP_PORT_ANY) ||
11542 !IN_LEV_FIELD(nextx, nexty) ||
11543 !IS_FREE(nextx, nexty))
11544 return MF_NO_ACTION;
11547 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11548 element == EL_SP_GRAVITY_PORT_RIGHT ||
11549 element == EL_SP_GRAVITY_PORT_UP ||
11550 element == EL_SP_GRAVITY_PORT_DOWN)
11551 game.gravity = !game.gravity;
11553 /* automatically move to the next field with double speed */
11554 player->programmed_action = move_direction;
11556 if (player->move_delay_reset_counter == 0)
11558 player->move_delay_reset_counter = 2; /* two double speed steps */
11560 DOUBLE_PLAYER_SPEED(player);
11563 player->move_delay_reset_counter = 2;
11565 DOUBLE_PLAYER_SPEED(player);
11569 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11572 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11578 case EL_TUBE_VERTICAL:
11579 case EL_TUBE_HORIZONTAL:
11580 case EL_TUBE_VERTICAL_LEFT:
11581 case EL_TUBE_VERTICAL_RIGHT:
11582 case EL_TUBE_HORIZONTAL_UP:
11583 case EL_TUBE_HORIZONTAL_DOWN:
11584 case EL_TUBE_LEFT_UP:
11585 case EL_TUBE_LEFT_DOWN:
11586 case EL_TUBE_RIGHT_UP:
11587 case EL_TUBE_RIGHT_DOWN:
11590 int tube_enter_directions[][2] =
11592 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11593 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11594 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11595 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11596 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11597 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11598 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11599 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11600 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11601 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11602 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11603 { -1, MV_NO_MOVING }
11606 while (tube_enter_directions[i][0] != element)
11609 if (tube_enter_directions[i][0] == -1) /* should not happen */
11613 if (!(tube_enter_directions[i][1] & move_direction))
11614 return MF_NO_ACTION; /* tube has no opening in this direction */
11616 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11624 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11626 if (IS_WALKABLE(element))
11629 int sound_element = SND_ELEMENT(element);
11630 int sound_action = ACTION_WALKING;
11633 if (!ACCESS_FROM(element, opposite_direction))
11634 return MF_NO_ACTION; /* field not accessible from this direction */
11638 if (element == EL_EMPTY_SPACE &&
11639 game.gravity && !player->is_auto_moving &&
11640 canFallDown(player) && move_direction != MV_DOWN)
11641 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11644 if (IS_RND_GATE(element))
11646 if (!player->key[RND_GATE_NR(element)])
11647 return MF_NO_ACTION;
11649 else if (IS_RND_GATE_GRAY(element))
11651 if (!player->key[RND_GATE_GRAY_NR(element)])
11652 return MF_NO_ACTION;
11654 else if (element == EL_EXIT_OPEN ||
11655 element == EL_SP_EXIT_OPEN ||
11656 element == EL_SP_EXIT_OPENING)
11658 sound_action = ACTION_PASSING; /* player is passing exit */
11660 else if (element == EL_EMPTY)
11662 sound_action = ACTION_MOVING; /* nothing to walk on */
11665 /* play sound from background or player, whatever is available */
11666 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11667 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11669 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11674 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11676 else if (IS_PASSABLE(element))
11680 if (!canPassField(x, y, move_direction))
11681 return MF_NO_ACTION;
11686 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11687 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11688 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11689 return MF_NO_ACTION;
11691 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11692 return MF_NO_ACTION;
11697 if (!ACCESS_FROM(element, opposite_direction))
11698 return MF_NO_ACTION; /* field not accessible from this direction */
11700 if (IS_CUSTOM_ELEMENT(element) &&
11701 !ACCESS_FROM(element, opposite_direction))
11702 return MF_NO_ACTION; /* field not accessible from this direction */
11706 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11707 return MF_NO_ACTION;
11712 if (IS_EM_GATE(element))
11714 if (!player->key[EM_GATE_NR(element)])
11715 return MF_NO_ACTION;
11717 else if (IS_EM_GATE_GRAY(element))
11719 if (!player->key[EM_GATE_GRAY_NR(element)])
11720 return MF_NO_ACTION;
11722 else if (IS_SP_PORT(element))
11724 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11725 element == EL_SP_GRAVITY_PORT_RIGHT ||
11726 element == EL_SP_GRAVITY_PORT_UP ||
11727 element == EL_SP_GRAVITY_PORT_DOWN)
11728 game.gravity = !game.gravity;
11729 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11730 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11731 element == EL_SP_GRAVITY_ON_PORT_UP ||
11732 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11733 game.gravity = TRUE;
11734 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11735 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11736 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11737 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11738 game.gravity = FALSE;
11741 /* automatically move to the next field with double speed */
11742 player->programmed_action = move_direction;
11744 if (player->move_delay_reset_counter == 0)
11746 player->move_delay_reset_counter = 2; /* two double speed steps */
11748 DOUBLE_PLAYER_SPEED(player);
11751 player->move_delay_reset_counter = 2;
11753 DOUBLE_PLAYER_SPEED(player);
11756 PlayLevelSoundAction(x, y, ACTION_PASSING);
11760 else if (IS_DIGGABLE(element))
11764 if (mode != DF_SNAP)
11767 GfxElement[x][y] = GFX_ELEMENT(element);
11770 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11772 player->is_digging = TRUE;
11775 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11777 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11778 player->index_bit, dig_side);
11781 if (mode == DF_SNAP)
11782 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11787 else if (IS_COLLECTIBLE(element))
11791 if (is_player && mode != DF_SNAP)
11793 GfxElement[x][y] = element;
11794 player->is_collecting = TRUE;
11797 if (element == EL_SPEED_PILL)
11798 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11799 else if (element == EL_EXTRA_TIME && level.time > 0)
11802 DrawGameValue_Time(TimeLeft);
11804 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11806 player->shield_normal_time_left += 10;
11807 if (element == EL_SHIELD_DEADLY)
11808 player->shield_deadly_time_left += 10;
11810 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11812 if (player->inventory_size < MAX_INVENTORY_SIZE)
11813 player->inventory_element[player->inventory_size++] = element;
11815 DrawGameValue_Dynamite(local_player->inventory_size);
11817 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11819 player->dynabomb_count++;
11820 player->dynabombs_left++;
11822 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11824 player->dynabomb_size++;
11826 else if (element == EL_DYNABOMB_INCREASE_POWER)
11828 player->dynabomb_xl = TRUE;
11830 else if (IS_KEY(element))
11832 player->key[KEY_NR(element)] = TRUE;
11834 DrawGameValue_Keys(player->key);
11836 redraw_mask |= REDRAW_DOOR_1;
11838 else if (IS_ENVELOPE(element))
11841 player->show_envelope = element;
11843 ShowEnvelope(element - EL_ENVELOPE_1);
11846 else if (IS_DROPPABLE(element) ||
11847 IS_THROWABLE(element)) /* can be collected and dropped */
11851 if (element_info[element].collect_count == 0)
11852 player->inventory_infinite_element = element;
11854 for (i = 0; i < element_info[element].collect_count; i++)
11855 if (player->inventory_size < MAX_INVENTORY_SIZE)
11856 player->inventory_element[player->inventory_size++] = element;
11858 DrawGameValue_Dynamite(local_player->inventory_size);
11860 else if (element_info[element].collect_count > 0)
11862 local_player->gems_still_needed -=
11863 element_info[element].collect_count;
11864 if (local_player->gems_still_needed < 0)
11865 local_player->gems_still_needed = 0;
11867 DrawGameValue_Emeralds(local_player->gems_still_needed);
11870 RaiseScoreElement(element);
11871 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11874 CheckTriggeredElementChangeByPlayer(x, y, element,
11875 CE_PLAYER_COLLECTS_X,
11876 player->index_bit, dig_side);
11879 if (mode == DF_SNAP)
11880 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11885 else if (IS_PUSHABLE(element))
11887 if (mode == DF_SNAP && element != EL_BD_ROCK)
11888 return MF_NO_ACTION;
11890 if (CAN_FALL(element) && dy)
11891 return MF_NO_ACTION;
11893 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11894 !(element == EL_SPRING && level.use_spring_bug))
11895 return MF_NO_ACTION;
11898 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11899 ((move_direction & MV_VERTICAL &&
11900 ((element_info[element].move_pattern & MV_LEFT &&
11901 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11902 (element_info[element].move_pattern & MV_RIGHT &&
11903 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11904 (move_direction & MV_HORIZONTAL &&
11905 ((element_info[element].move_pattern & MV_UP &&
11906 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11907 (element_info[element].move_pattern & MV_DOWN &&
11908 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11909 return MF_NO_ACTION;
11913 /* do not push elements already moving away faster than player */
11914 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11915 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11916 return MF_NO_ACTION;
11918 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11919 return MF_NO_ACTION;
11925 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11927 if (player->push_delay_value == -1 || !player_was_pushing)
11928 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11930 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11932 if (player->push_delay_value == -1)
11933 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11936 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11938 if (player->push_delay_value == -1 || !player_was_pushing)
11939 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11942 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11944 if (!player->is_pushing)
11945 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11949 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11950 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11951 !player_is_pushing))
11952 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11955 if (!player->is_pushing &&
11956 game.engine_version >= VERSION_IDENT(2,2,0,7))
11957 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11961 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11962 player->push_delay, player->push_delay_value,
11963 FrameCounter, game.engine_version,
11964 player_was_pushing, player->is_pushing,
11965 element, element_info[element].token_name,
11966 GET_NEW_PUSH_DELAY(element));
11969 player->is_pushing = TRUE;
11971 if (!(IN_LEV_FIELD(nextx, nexty) &&
11972 (IS_FREE(nextx, nexty) ||
11973 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11974 IS_SB_ELEMENT(element)))))
11975 return MF_NO_ACTION;
11977 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11978 return MF_NO_ACTION;
11980 #if USE_NEW_PUSH_DELAY
11983 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11984 printf("::: ALERT: %d, %d [%d / %d]\n",
11985 player->push_delay, player->push_delay2,
11986 FrameCounter, FrameCounter / 50);
11989 if (player->push_delay == -1) /* new pushing; restart delay */
11990 player->push_delay = 0;
11992 if (player->push_delay == 0) /* new pushing; restart delay */
11993 player->push_delay = FrameCounter;
11996 #if USE_NEW_PUSH_DELAY
11998 if ( (player->push_delay > 0) != (!xxx_fr) )
11999 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
12000 player->push_delay,
12001 xxx_pdv2, player->push_delay2, player->push_delay_value,
12002 FrameCounter, FrameCounter / 50);
12006 if (player->push_delay > 0 &&
12007 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12008 element != EL_SPRING && element != EL_BALLOON)
12011 if (player->push_delay < player->push_delay_value &&
12012 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12013 element != EL_SPRING && element != EL_BALLOON)
12017 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12018 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12019 element != EL_SPRING && element != EL_BALLOON)
12022 /* make sure that there is no move delay before next try to push */
12023 #if USE_NEW_MOVE_DELAY
12024 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12025 player->move_delay = 0;
12027 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12028 player->move_delay = INITIAL_MOVE_DELAY_OFF;
12031 return MF_NO_ACTION;
12035 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12038 if (IS_SB_ELEMENT(element))
12040 if (element == EL_SOKOBAN_FIELD_FULL)
12042 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12043 local_player->sokobanfields_still_needed++;
12046 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12048 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12049 local_player->sokobanfields_still_needed--;
12052 Feld[x][y] = EL_SOKOBAN_OBJECT;
12054 if (Back[x][y] == Back[nextx][nexty])
12055 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12056 else if (Back[x][y] != 0)
12057 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12060 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12063 if (local_player->sokobanfields_still_needed == 0 &&
12064 game.emulation == EMU_SOKOBAN)
12066 player->LevelSolved = player->GameOver = TRUE;
12067 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12071 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12073 InitMovingField(x, y, move_direction);
12074 GfxAction[x][y] = ACTION_PUSHING;
12076 if (mode == DF_SNAP)
12077 ContinueMoving(x, y);
12079 MovPos[x][y] = (dx != 0 ? dx : dy);
12081 Pushed[x][y] = TRUE;
12082 Pushed[nextx][nexty] = TRUE;
12084 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12085 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12087 player->push_delay_value = -1; /* get new value later */
12089 #if USE_PUSH_BUGFIX
12090 /* now: check for element change _after_ element has been pushed! */
12092 if (game.use_change_when_pushing_bug)
12094 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12097 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12098 player->index_bit, dig_side);
12099 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12100 player->index_bit, dig_side);
12106 /* check for element change _after_ element has been pushed! */
12110 /* !!! TEST ONLY !!! */
12111 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12112 player->index_bit, dig_side);
12113 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12114 player->index_bit, dig_side);
12116 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12117 player->index_bit, dig_side);
12118 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12119 player->index_bit, dig_side);
12127 else if (IS_SWITCHABLE(element))
12129 if (PLAYER_SWITCHING(player, x, y))
12131 CheckTriggeredElementChangeByPlayer(x,y, element,
12132 CE_PLAYER_PRESSES_X,
12133 player->index_bit, dig_side);
12138 player->is_switching = TRUE;
12139 player->switch_x = x;
12140 player->switch_y = y;
12142 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12144 if (element == EL_ROBOT_WHEEL)
12146 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12150 DrawLevelField(x, y);
12152 else if (element == EL_SP_TERMINAL)
12156 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12158 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12160 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12161 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12164 else if (IS_BELT_SWITCH(element))
12166 ToggleBeltSwitch(x, y);
12168 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12169 element == EL_SWITCHGATE_SWITCH_DOWN)
12171 ToggleSwitchgateSwitch(x, y);
12173 else if (element == EL_LIGHT_SWITCH ||
12174 element == EL_LIGHT_SWITCH_ACTIVE)
12176 ToggleLightSwitch(x, y);
12179 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12180 SND_LIGHT_SWITCH_ACTIVATING :
12181 SND_LIGHT_SWITCH_DEACTIVATING);
12184 else if (element == EL_TIMEGATE_SWITCH)
12186 ActivateTimegateSwitch(x, y);
12188 else if (element == EL_BALLOON_SWITCH_LEFT ||
12189 element == EL_BALLOON_SWITCH_RIGHT ||
12190 element == EL_BALLOON_SWITCH_UP ||
12191 element == EL_BALLOON_SWITCH_DOWN ||
12192 element == EL_BALLOON_SWITCH_ANY)
12194 if (element == EL_BALLOON_SWITCH_ANY)
12195 game.balloon_dir = move_direction;
12197 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12198 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12199 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12200 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12203 else if (element == EL_LAMP)
12205 Feld[x][y] = EL_LAMP_ACTIVE;
12206 local_player->lights_still_needed--;
12208 ResetGfxAnimation(x, y);
12209 DrawLevelField(x, y);
12211 else if (element == EL_TIME_ORB_FULL)
12213 Feld[x][y] = EL_TIME_ORB_EMPTY;
12215 DrawGameValue_Time(TimeLeft);
12217 ResetGfxAnimation(x, y);
12218 DrawLevelField(x, y);
12221 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12225 CheckTriggeredElementChangeByPlayer(x, y, element,
12227 player->index_bit, dig_side);
12229 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12230 player->index_bit, dig_side);
12236 if (!PLAYER_SWITCHING(player, x, y))
12238 player->is_switching = TRUE;
12239 player->switch_x = x;
12240 player->switch_y = y;
12243 /* !!! TEST ONLY !!! */
12244 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12245 player->index_bit, dig_side);
12246 CheckTriggeredElementChangeByPlayer(x, y, element,
12248 player->index_bit, dig_side);
12250 CheckTriggeredElementChangeByPlayer(x, y, element,
12252 player->index_bit, dig_side);
12253 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12254 player->index_bit, dig_side);
12259 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12260 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12261 player->index_bit, dig_side);
12262 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12263 player->index_bit, dig_side);
12265 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12266 player->index_bit, dig_side);
12267 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12268 player->index_bit, dig_side);
12272 return MF_NO_ACTION;
12275 #if USE_NEW_PUSH_DELAY
12276 player->push_delay = -1;
12278 player->push_delay = 0;
12281 #if USE_PENGUIN_COLLECT_BUGFIX
12282 if (is_player) /* function can also be called by EL_PENGUIN */
12285 if (Feld[x][y] != element) /* really digged/collected something */
12286 player->is_collecting = !player->is_digging;
12292 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12294 int jx = player->jx, jy = player->jy;
12295 int x = jx + dx, y = jy + dy;
12296 int snap_direction = (dx == -1 ? MV_LEFT :
12297 dx == +1 ? MV_RIGHT :
12299 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12302 if (player->MovPos != 0)
12305 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12309 if (!player->active || !IN_LEV_FIELD(x, y))
12317 if (player->MovPos == 0)
12318 player->is_pushing = FALSE;
12320 player->is_snapping = FALSE;
12322 if (player->MovPos == 0)
12324 player->is_moving = FALSE;
12325 player->is_digging = FALSE;
12326 player->is_collecting = FALSE;
12332 if (player->is_snapping)
12335 player->MovDir = snap_direction;
12338 if (player->MovPos == 0)
12341 player->is_moving = FALSE;
12342 player->is_digging = FALSE;
12343 player->is_collecting = FALSE;
12346 player->is_dropping = FALSE;
12348 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12351 player->is_snapping = TRUE;
12354 if (player->MovPos == 0)
12357 player->is_moving = FALSE;
12358 player->is_digging = FALSE;
12359 player->is_collecting = FALSE;
12363 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12364 DrawLevelField(player->last_jx, player->last_jy);
12367 DrawLevelField(x, y);
12376 boolean DropElement(struct PlayerInfo *player)
12378 int old_element, new_element;
12379 int dropx = player->jx, dropy = player->jy;
12380 int drop_direction = player->MovDir;
12382 int drop_side = drop_direction;
12384 static int trigger_sides[4] =
12386 CH_SIDE_LEFT, /* dropping left */
12387 CH_SIDE_RIGHT, /* dropping right */
12388 CH_SIDE_TOP, /* dropping up */
12389 CH_SIDE_BOTTOM, /* dropping down */
12391 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12393 int drop_element = (player->inventory_size > 0 ?
12394 player->inventory_element[player->inventory_size - 1] :
12395 player->inventory_infinite_element != EL_UNDEFINED ?
12396 player->inventory_infinite_element :
12397 player->dynabombs_left > 0 ?
12398 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12401 #if USE_DROP_BUGFIX
12402 /* do not drop an element on top of another element; when holding drop key
12403 pressed without moving, dropped element must move away before the next
12404 element can be dropped (this is especially important if the next element
12405 is dynamite, which can be placed on background for historical reasons) */
12406 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12410 if (IS_THROWABLE(drop_element))
12412 dropx += GET_DX_FROM_DIR(drop_direction);
12413 dropy += GET_DY_FROM_DIR(drop_direction);
12415 if (!IN_LEV_FIELD(dropx, dropy))
12419 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12420 new_element = drop_element; /* default: no change when dropping */
12422 /* check if player is active, not moving and ready to drop */
12423 if (!player->active || player->MovPos || player->drop_delay > 0)
12426 /* check if player has anything that can be dropped */
12428 if (new_element == EL_UNDEFINED)
12431 if (player->inventory_size == 0 &&
12432 player->inventory_infinite_element == EL_UNDEFINED &&
12433 player->dynabombs_left == 0)
12437 /* check if anything can be dropped at the current position */
12438 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12441 /* collected custom elements can only be dropped on empty fields */
12443 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12446 if (player->inventory_size > 0 &&
12447 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12448 && old_element != EL_EMPTY)
12452 if (old_element != EL_EMPTY)
12453 Back[dropx][dropy] = old_element; /* store old element on this field */
12455 ResetGfxAnimation(dropx, dropy);
12456 ResetRandomAnimationValue(dropx, dropy);
12458 if (player->inventory_size > 0 ||
12459 player->inventory_infinite_element != EL_UNDEFINED)
12461 if (player->inventory_size > 0)
12463 player->inventory_size--;
12466 new_element = player->inventory_element[player->inventory_size];
12469 DrawGameValue_Dynamite(local_player->inventory_size);
12471 if (new_element == EL_DYNAMITE)
12472 new_element = EL_DYNAMITE_ACTIVE;
12473 else if (new_element == EL_SP_DISK_RED)
12474 new_element = EL_SP_DISK_RED_ACTIVE;
12477 Feld[dropx][dropy] = new_element;
12479 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12480 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12481 el2img(Feld[dropx][dropy]), 0);
12483 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12486 /* needed if previous element just changed to "empty" in the last frame */
12487 Changed[dropx][dropy] = FALSE; /* allow another change */
12491 /* !!! TEST ONLY !!! */
12492 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12493 player->index_bit, drop_side);
12494 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12496 player->index_bit, drop_side);
12498 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12500 player->index_bit, drop_side);
12501 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12502 player->index_bit, drop_side);
12505 TestIfElementTouchesCustomElement(dropx, dropy);
12507 else /* player is dropping a dyna bomb */
12509 player->dynabombs_left--;
12512 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12515 Feld[dropx][dropy] = new_element;
12517 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12518 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12519 el2img(Feld[dropx][dropy]), 0);
12521 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12528 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12531 InitField_WithBug1(dropx, dropy, FALSE);
12533 InitField(dropx, dropy, FALSE);
12534 if (CAN_MOVE(Feld[dropx][dropy]))
12535 InitMovDir(dropx, dropy);
12539 new_element = Feld[dropx][dropy]; /* element might have changed */
12541 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12542 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12545 int move_stepsize = element_info[new_element].move_stepsize;
12547 int move_direction, nextx, nexty;
12549 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12550 MovDir[dropx][dropy] = drop_direction;
12552 move_direction = MovDir[dropx][dropy];
12553 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12554 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12557 Changed[dropx][dropy] = FALSE; /* allow another change */
12558 CheckCollision[dropx][dropy] = 2;
12561 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12564 WasJustMoving[dropx][dropy] = 3;
12567 InitMovingField(dropx, dropy, move_direction);
12568 ContinueMoving(dropx, dropy);
12573 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12576 Changed[dropx][dropy] = FALSE; /* allow another change */
12579 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12581 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12582 CE_HITTING_SOMETHING, move_direction);
12590 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12595 player->drop_delay = 8 + 8 + 8;
12599 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12604 player->is_dropping = TRUE;
12606 #if USE_DROP_BUGFIX
12607 player->drop_x = dropx;
12608 player->drop_y = dropy;
12614 /* ------------------------------------------------------------------------- */
12615 /* game sound playing functions */
12616 /* ------------------------------------------------------------------------- */
12618 static int *loop_sound_frame = NULL;
12619 static int *loop_sound_volume = NULL;
12621 void InitPlayLevelSound()
12623 int num_sounds = getSoundListSize();
12625 checked_free(loop_sound_frame);
12626 checked_free(loop_sound_volume);
12628 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12629 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12632 static void PlayLevelSound(int x, int y, int nr)
12634 int sx = SCREENX(x), sy = SCREENY(y);
12635 int volume, stereo_position;
12636 int max_distance = 8;
12637 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12639 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12640 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12643 if (!IN_LEV_FIELD(x, y) ||
12644 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12645 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12648 volume = SOUND_MAX_VOLUME;
12650 if (!IN_SCR_FIELD(sx, sy))
12652 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12653 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12655 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12658 stereo_position = (SOUND_MAX_LEFT +
12659 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12660 (SCR_FIELDX + 2 * max_distance));
12662 if (IS_LOOP_SOUND(nr))
12664 /* This assures that quieter loop sounds do not overwrite louder ones,
12665 while restarting sound volume comparison with each new game frame. */
12667 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12670 loop_sound_volume[nr] = volume;
12671 loop_sound_frame[nr] = FrameCounter;
12674 PlaySoundExt(nr, volume, stereo_position, type);
12677 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12679 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12680 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12681 y < LEVELY(BY1) ? LEVELY(BY1) :
12682 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12686 static void PlayLevelSoundAction(int x, int y, int action)
12688 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12691 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12693 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12695 if (sound_effect != SND_UNDEFINED)
12696 PlayLevelSound(x, y, sound_effect);
12699 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12702 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12704 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12705 PlayLevelSound(x, y, sound_effect);
12708 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12710 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12712 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12713 PlayLevelSound(x, y, sound_effect);
12716 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12718 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12720 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12721 StopSound(sound_effect);
12724 static void PlayLevelMusic()
12726 if (levelset.music[level_nr] != MUS_UNDEFINED)
12727 PlayMusic(levelset.music[level_nr]); /* from config file */
12729 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12732 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12734 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12737 if (sample == SAMPLE_bug)
12738 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12744 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12748 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12752 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12756 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12760 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12764 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12768 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12771 case SAMPLE_android_clone:
12772 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12775 case SAMPLE_android_move:
12776 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12779 case SAMPLE_spring:
12780 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12784 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12788 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12791 case SAMPLE_eater_eat:
12792 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12796 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12799 case SAMPLE_collect:
12800 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12803 case SAMPLE_diamond:
12804 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12807 case SAMPLE_squash:
12808 /* !!! CHECK THIS !!! */
12810 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12812 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12816 case SAMPLE_wonderfall:
12817 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12821 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12825 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12829 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12833 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12837 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12841 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12844 case SAMPLE_wonder:
12845 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12849 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12852 case SAMPLE_exit_open:
12853 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12856 case SAMPLE_exit_leave:
12857 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12860 case SAMPLE_dynamite:
12861 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12865 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12869 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12873 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12877 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12881 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12885 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12889 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12894 void RaiseScore(int value)
12896 local_player->score += value;
12898 DrawGameValue_Score(local_player->score);
12901 void RaiseScoreElement(int element)
12906 case EL_BD_DIAMOND:
12907 case EL_EMERALD_YELLOW:
12908 case EL_EMERALD_RED:
12909 case EL_EMERALD_PURPLE:
12910 case EL_SP_INFOTRON:
12911 RaiseScore(level.score[SC_EMERALD]);
12914 RaiseScore(level.score[SC_DIAMOND]);
12917 RaiseScore(level.score[SC_CRYSTAL]);
12920 RaiseScore(level.score[SC_PEARL]);
12923 case EL_BD_BUTTERFLY:
12924 case EL_SP_ELECTRON:
12925 RaiseScore(level.score[SC_BUG]);
12928 case EL_BD_FIREFLY:
12929 case EL_SP_SNIKSNAK:
12930 RaiseScore(level.score[SC_SPACESHIP]);
12933 case EL_DARK_YAMYAM:
12934 RaiseScore(level.score[SC_YAMYAM]);
12937 RaiseScore(level.score[SC_ROBOT]);
12940 RaiseScore(level.score[SC_PACMAN]);
12943 RaiseScore(level.score[SC_NUT]);
12946 case EL_SP_DISK_RED:
12947 case EL_DYNABOMB_INCREASE_NUMBER:
12948 case EL_DYNABOMB_INCREASE_SIZE:
12949 case EL_DYNABOMB_INCREASE_POWER:
12950 RaiseScore(level.score[SC_DYNAMITE]);
12952 case EL_SHIELD_NORMAL:
12953 case EL_SHIELD_DEADLY:
12954 RaiseScore(level.score[SC_SHIELD]);
12956 case EL_EXTRA_TIME:
12957 RaiseScore(level.score[SC_TIME_BONUS]);
12971 RaiseScore(level.score[SC_KEY]);
12974 RaiseScore(element_info[element].collect_score);
12979 void RequestQuitGame(boolean ask_if_really_quit)
12981 if (AllPlayersGone ||
12982 !ask_if_really_quit ||
12983 level_editor_test_game ||
12984 Request("Do you really want to quit the game ?",
12985 REQ_ASK | REQ_STAY_CLOSED))
12987 #if defined(NETWORK_AVALIABLE)
12988 if (options.network)
12989 SendToServer_StopPlaying();
12993 game_status = GAME_MODE_MAIN;
13001 if (tape.playing && tape.deactivate_display)
13002 TapeDeactivateDisplayOff(TRUE);
13005 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13008 if (tape.playing && tape.deactivate_display)
13009 TapeDeactivateDisplayOn();
13016 /* ---------- new game button stuff ---------------------------------------- */
13018 /* graphic position values for game buttons */
13019 #define GAME_BUTTON_XSIZE 30
13020 #define GAME_BUTTON_YSIZE 30
13021 #define GAME_BUTTON_XPOS 5
13022 #define GAME_BUTTON_YPOS 215
13023 #define SOUND_BUTTON_XPOS 5
13024 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13026 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13027 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13028 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13029 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13030 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13031 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13038 } gamebutton_info[NUM_GAME_BUTTONS] =
13041 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13046 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13047 GAME_CTRL_ID_PAUSE,
13051 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13056 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13057 SOUND_CTRL_ID_MUSIC,
13058 "background music on/off"
13061 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13062 SOUND_CTRL_ID_LOOPS,
13063 "sound loops on/off"
13066 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13067 SOUND_CTRL_ID_SIMPLE,
13068 "normal sounds on/off"
13072 void CreateGameButtons()
13076 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13078 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13079 struct GadgetInfo *gi;
13082 unsigned long event_mask;
13083 int gd_xoffset, gd_yoffset;
13084 int gd_x1, gd_x2, gd_y1, gd_y2;
13087 gd_xoffset = gamebutton_info[i].x;
13088 gd_yoffset = gamebutton_info[i].y;
13089 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13090 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13092 if (id == GAME_CTRL_ID_STOP ||
13093 id == GAME_CTRL_ID_PAUSE ||
13094 id == GAME_CTRL_ID_PLAY)
13096 button_type = GD_TYPE_NORMAL_BUTTON;
13098 event_mask = GD_EVENT_RELEASED;
13099 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13100 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13104 button_type = GD_TYPE_CHECK_BUTTON;
13106 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13107 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13108 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13109 event_mask = GD_EVENT_PRESSED;
13110 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13111 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13114 gi = CreateGadget(GDI_CUSTOM_ID, id,
13115 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13116 GDI_X, DX + gd_xoffset,
13117 GDI_Y, DY + gd_yoffset,
13118 GDI_WIDTH, GAME_BUTTON_XSIZE,
13119 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13120 GDI_TYPE, button_type,
13121 GDI_STATE, GD_BUTTON_UNPRESSED,
13122 GDI_CHECKED, checked,
13123 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13124 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13125 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13126 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13127 GDI_EVENT_MASK, event_mask,
13128 GDI_CALLBACK_ACTION, HandleGameButtons,
13132 Error(ERR_EXIT, "cannot create gadget");
13134 game_gadget[id] = gi;
13138 void FreeGameButtons()
13142 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13143 FreeGadget(game_gadget[i]);
13146 static void MapGameButtons()
13150 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13151 MapGadget(game_gadget[i]);
13154 void UnmapGameButtons()
13158 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13159 UnmapGadget(game_gadget[i]);
13162 static void HandleGameButtons(struct GadgetInfo *gi)
13164 int id = gi->custom_id;
13166 if (game_status != GAME_MODE_PLAYING)
13171 case GAME_CTRL_ID_STOP:
13172 RequestQuitGame(TRUE);
13175 case GAME_CTRL_ID_PAUSE:
13176 if (options.network)
13178 #if defined(NETWORK_AVALIABLE)
13180 SendToServer_ContinuePlaying();
13182 SendToServer_PausePlaying();
13186 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13189 case GAME_CTRL_ID_PLAY:
13192 #if defined(NETWORK_AVALIABLE)
13193 if (options.network)
13194 SendToServer_ContinuePlaying();
13198 tape.pausing = FALSE;
13199 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13204 case SOUND_CTRL_ID_MUSIC:
13205 if (setup.sound_music)
13207 setup.sound_music = FALSE;
13210 else if (audio.music_available)
13212 setup.sound = setup.sound_music = TRUE;
13214 SetAudioMode(setup.sound);
13220 case SOUND_CTRL_ID_LOOPS:
13221 if (setup.sound_loops)
13222 setup.sound_loops = FALSE;
13223 else if (audio.loops_available)
13225 setup.sound = setup.sound_loops = TRUE;
13226 SetAudioMode(setup.sound);
13230 case SOUND_CTRL_ID_SIMPLE:
13231 if (setup.sound_simple)
13232 setup.sound_simple = FALSE;
13233 else if (audio.sound_available)
13235 setup.sound = setup.sound_simple = TRUE;
13236 SetAudioMode(setup.sound);