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 HandleChangeAction(int change_action)
7864 if (change_action == CA_EXIT_PLAYER)
7866 printf("::: CA_EXIT_GAME\n");
7868 /* !!! local_player <-> 4 players !!! (EXTEND THIS) !!! */
7869 local_player->LevelSolved = local_player->GameOver = TRUE;
7871 else if (change_action == CA_KILL_PLAYER)
7873 printf("::: CA_KILL_PLAYER\n");
7875 /* !!! local_player <-> 4 players !!! (EXTEND THIS) !!! */
7876 KillHero(local_player);
7880 static void ChangeElementNowExt(struct ElementChangeInfo *change,
7881 int x, int y, int target_element)
7883 int previous_move_direction = MovDir[x][y];
7885 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7886 IS_WALKABLE(Feld[x][y]));
7888 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7889 IS_WALKABLE(Feld[x][y]) &&
7893 /* check if element under player changes from accessible to unaccessible
7894 (needed for special case of dropping element which then changes) */
7895 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7896 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7899 printf("::: BOOOM! [%d, '%s']\n", target_element,
7900 element_info[target_element].token_name);
7912 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7913 RemoveMovingField(x, y);
7917 Feld[x][y] = target_element;
7920 Feld[x][y] = target_element;
7923 ResetGfxAnimation(x, y);
7924 ResetRandomAnimationValue(x, y);
7926 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7927 MovDir[x][y] = previous_move_direction;
7930 InitField_WithBug1(x, y, FALSE);
7932 InitField(x, y, FALSE);
7933 if (CAN_MOVE(Feld[x][y]))
7937 DrawLevelField(x, y);
7939 if (GFX_CRUMBLED(Feld[x][y]))
7940 DrawLevelFieldCrumbledSandNeighbours(x, y);
7944 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7948 TestIfBadThingTouchesHero(x, y);
7949 TestIfPlayerTouchesCustomElement(x, y);
7950 TestIfElementTouchesCustomElement(x, y);
7953 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7954 if (ELEM_IS_PLAYER(target_element))
7955 RelocatePlayer(x, y, target_element);
7958 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7960 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7964 TestIfBadThingTouchesHero(x, y);
7965 TestIfPlayerTouchesCustomElement(x, y);
7966 TestIfElementTouchesCustomElement(x, y);
7969 if (change->use_change_action)
7970 HandleChangeAction(change->change_action);
7973 static boolean ChangeElementNow(int x, int y, int element, int page)
7975 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7977 int old_element = Feld[x][y];
7979 /* always use default change event to prevent running into a loop */
7980 if (ChangeEvent[x][y] == -1)
7981 ChangeEvent[x][y] = CE_DELAY;
7983 if (ChangeEvent[x][y] == CE_DELAY)
7985 /* reset actual trigger element and player */
7986 change->actual_trigger_element = EL_EMPTY;
7987 change->actual_trigger_player = EL_PLAYER_1;
7991 /* do not change any elements that have already changed in this frame */
7995 /* do not change already changed elements with same change event */
7996 if (Changed[x][y] & ChangeEvent[x][y])
8001 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
8003 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
8007 /* !!! indirect change before direct change !!! */
8008 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
8011 if (change->explode)
8018 if (change->use_target_content)
8020 boolean complete_replace = TRUE;
8021 boolean can_replace[3][3];
8024 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8027 boolean is_walkable;
8028 boolean is_diggable;
8029 boolean is_collectible;
8030 boolean is_removable;
8031 boolean is_destructible;
8032 int ex = x + xx - 1;
8033 int ey = y + yy - 1;
8034 int content_element = change->target_content[xx][yy];
8037 can_replace[xx][yy] = TRUE;
8039 if (ex == x && ey == y) /* do not check changing element itself */
8042 if (content_element == EL_EMPTY_SPACE)
8044 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8049 if (!IN_LEV_FIELD(ex, ey))
8051 can_replace[xx][yy] = FALSE;
8052 complete_replace = FALSE;
8058 if (Changed[ex][ey]) /* do not change already changed elements */
8060 can_replace[xx][yy] = FALSE;
8061 complete_replace = FALSE;
8069 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8070 e = MovingOrBlocked2Element(ex, ey);
8075 is_empty = (IS_FREE(ex, ey) ||
8076 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8077 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8078 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8082 is_empty = (IS_FREE(ex, ey) ||
8083 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8085 is_empty = (IS_FREE(ex, ey) ||
8086 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8091 is_walkable = (is_empty || IS_WALKABLE(e));
8092 is_diggable = (is_empty || IS_DIGGABLE(e));
8093 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8094 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8095 is_removable = (is_diggable || is_collectible);
8097 can_replace[xx][yy] =
8098 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8099 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8100 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8101 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8102 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8103 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8104 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8106 if (!can_replace[xx][yy])
8107 complete_replace = FALSE;
8109 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8110 IS_WALKABLE(content_element)));
8112 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8114 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8117 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8118 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8119 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8121 can_replace[xx][yy] = FALSE;
8122 complete_replace = FALSE;
8127 if (!change->only_if_complete || complete_replace)
8129 boolean something_has_changed = FALSE;
8131 if (change->only_if_complete && change->use_random_replace &&
8132 RND(100) < change->random_percentage)
8135 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8137 int ex = x + xx - 1;
8138 int ey = y + yy - 1;
8139 int content_element;
8141 if (can_replace[xx][yy] && (!change->use_random_replace ||
8142 RND(100) < change->random_percentage))
8144 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8145 RemoveMovingField(ex, ey);
8147 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8149 content_element = change->target_content[xx][yy];
8150 target_element = GET_TARGET_ELEMENT(content_element, change);
8152 ChangeElementNowExt(change, ex, ey, target_element);
8154 something_has_changed = TRUE;
8156 /* for symmetry reasons, freeze newly created border elements */
8157 if (ex != x || ey != y)
8158 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8162 if (something_has_changed)
8163 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8168 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8170 ChangeElementNowExt(change, x, y, target_element);
8172 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8176 /* this uses direct change before indirect change */
8177 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8183 static void ChangeElement(int x, int y, int page)
8185 int element = MovingOrBlocked2Element(x, y);
8186 struct ElementInfo *ei = &element_info[element];
8187 struct ElementChangeInfo *change = &ei->change_page[page];
8190 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8193 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8194 x, y, element, element_info[element].token_name);
8195 printf("ChangeElement(): This should never happen!\n");
8200 /* this can happen with classic bombs on walkable, changing elements */
8201 if (!CAN_CHANGE(element))
8204 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8205 ChangeDelay[x][y] = 0;
8211 if (ChangeDelay[x][y] == 0) /* initialize element change */
8213 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8214 RND(change->delay_random * change->delay_frames)) + 1;
8216 ResetGfxAnimation(x, y);
8217 ResetRandomAnimationValue(x, y);
8219 if (change->pre_change_function)
8220 change->pre_change_function(x, y);
8223 ChangeDelay[x][y]--;
8225 if (ChangeDelay[x][y] != 0) /* continue element change */
8227 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8229 if (IS_ANIMATED(graphic))
8230 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8232 if (change->change_function)
8233 change->change_function(x, y);
8235 else /* finish element change */
8237 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8239 page = ChangePage[x][y];
8240 ChangePage[x][y] = -1;
8242 change = &ei->change_page[page];
8246 if (IS_MOVING(x, y) && !change->explode)
8248 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8251 ChangeDelay[x][y] = 1; /* try change after next move step */
8252 ChangePage[x][y] = page; /* remember page to use for change */
8257 if (ChangeElementNow(x, y, element, page))
8259 if (change->post_change_function)
8260 change->post_change_function(x, y);
8265 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8266 int trigger_element,
8273 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8275 if (!(trigger_events[trigger_element][trigger_event]))
8278 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8280 int element = EL_CUSTOM_START + i;
8282 boolean change_element = FALSE;
8285 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8288 for (j = 0; j < element_info[element].num_change_pages; j++)
8290 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8292 if (change->can_change &&
8293 change->has_event[trigger_event] &&
8294 change->trigger_side & trigger_side &&
8295 change->trigger_player & trigger_player &&
8296 change->trigger_page & trigger_page_bits &&
8297 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8300 if (!(change->has_event[trigger_event]))
8301 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8302 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8305 change_element = TRUE;
8308 change->actual_trigger_element = trigger_element;
8309 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8315 if (!change_element)
8318 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8321 if (x == lx && y == ly) /* do not change trigger element itself */
8325 if (Feld[x][y] == element)
8327 ChangeDelay[x][y] = 1;
8328 ChangeEvent[x][y] = trigger_event;
8329 ChangeElement(x, y, page);
8337 static boolean CheckElementChangeExt(int x, int y,
8339 int trigger_element,
8345 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8348 if (Feld[x][y] == EL_BLOCKED)
8350 Blocked2Moving(x, y, &x, &y);
8351 element = Feld[x][y];
8355 if (Feld[x][y] != element) /* check if element has already changed */
8358 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8359 Feld[x][y], element_info[Feld[x][y]].token_name,
8360 element, element_info[element].token_name,
8369 if (trigger_page < 0)
8371 boolean change_element = FALSE;
8374 for (i = 0; i < element_info[element].num_change_pages; i++)
8376 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8378 if (change->can_change &&
8379 change->has_event[trigger_event] &&
8380 change->trigger_side & trigger_side &&
8381 change->trigger_player & trigger_player)
8383 change_element = TRUE;
8386 change->actual_trigger_element = trigger_element;
8387 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8393 if (!change_element)
8398 struct ElementInfo *ei = &element_info[element];
8399 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8401 change->actual_trigger_element = trigger_element;
8402 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8407 /* !!! this check misses pages with same event, but different side !!! */
8409 if (trigger_page < 0)
8410 trigger_page = element_info[element].event_page_nr[trigger_event];
8412 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8416 ChangeDelay[x][y] = 1;
8417 ChangeEvent[x][y] = trigger_event;
8418 ChangeElement(x, y, trigger_page);
8423 static void PlayPlayerSound(struct PlayerInfo *player)
8425 int jx = player->jx, jy = player->jy;
8426 int element = player->element_nr;
8427 int last_action = player->last_action_waiting;
8428 int action = player->action_waiting;
8430 if (player->is_waiting)
8432 if (action != last_action)
8433 PlayLevelSoundElementAction(jx, jy, element, action);
8435 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8439 if (action != last_action)
8440 StopSound(element_info[element].sound[last_action]);
8442 if (last_action == ACTION_SLEEPING)
8443 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8447 static void PlayAllPlayersSound()
8451 for (i = 0; i < MAX_PLAYERS; i++)
8452 if (stored_player[i].active)
8453 PlayPlayerSound(&stored_player[i]);
8456 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8458 boolean last_waiting = player->is_waiting;
8459 int move_dir = player->MovDir;
8461 player->last_action_waiting = player->action_waiting;
8465 if (!last_waiting) /* not waiting -> waiting */
8467 player->is_waiting = TRUE;
8469 player->frame_counter_bored =
8471 game.player_boring_delay_fixed +
8472 SimpleRND(game.player_boring_delay_random);
8473 player->frame_counter_sleeping =
8475 game.player_sleeping_delay_fixed +
8476 SimpleRND(game.player_sleeping_delay_random);
8478 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8481 if (game.player_sleeping_delay_fixed +
8482 game.player_sleeping_delay_random > 0 &&
8483 player->anim_delay_counter == 0 &&
8484 player->post_delay_counter == 0 &&
8485 FrameCounter >= player->frame_counter_sleeping)
8486 player->is_sleeping = TRUE;
8487 else if (game.player_boring_delay_fixed +
8488 game.player_boring_delay_random > 0 &&
8489 FrameCounter >= player->frame_counter_bored)
8490 player->is_bored = TRUE;
8492 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8493 player->is_bored ? ACTION_BORING :
8496 if (player->is_sleeping)
8498 if (player->num_special_action_sleeping > 0)
8500 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8502 int last_special_action = player->special_action_sleeping;
8503 int num_special_action = player->num_special_action_sleeping;
8504 int special_action =
8505 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8506 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8507 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8508 last_special_action + 1 : ACTION_SLEEPING);
8509 int special_graphic =
8510 el_act_dir2img(player->element_nr, special_action, move_dir);
8512 player->anim_delay_counter =
8513 graphic_info[special_graphic].anim_delay_fixed +
8514 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8515 player->post_delay_counter =
8516 graphic_info[special_graphic].post_delay_fixed +
8517 SimpleRND(graphic_info[special_graphic].post_delay_random);
8519 player->special_action_sleeping = special_action;
8522 if (player->anim_delay_counter > 0)
8524 player->action_waiting = player->special_action_sleeping;
8525 player->anim_delay_counter--;
8527 else if (player->post_delay_counter > 0)
8529 player->post_delay_counter--;
8533 else if (player->is_bored)
8535 if (player->num_special_action_bored > 0)
8537 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8539 int special_action =
8540 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8541 int special_graphic =
8542 el_act_dir2img(player->element_nr, special_action, move_dir);
8544 player->anim_delay_counter =
8545 graphic_info[special_graphic].anim_delay_fixed +
8546 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8547 player->post_delay_counter =
8548 graphic_info[special_graphic].post_delay_fixed +
8549 SimpleRND(graphic_info[special_graphic].post_delay_random);
8551 player->special_action_bored = special_action;
8554 if (player->anim_delay_counter > 0)
8556 player->action_waiting = player->special_action_bored;
8557 player->anim_delay_counter--;
8559 else if (player->post_delay_counter > 0)
8561 player->post_delay_counter--;
8566 else if (last_waiting) /* waiting -> not waiting */
8568 player->is_waiting = FALSE;
8569 player->is_bored = FALSE;
8570 player->is_sleeping = FALSE;
8572 player->frame_counter_bored = -1;
8573 player->frame_counter_sleeping = -1;
8575 player->anim_delay_counter = 0;
8576 player->post_delay_counter = 0;
8578 player->action_waiting = ACTION_DEFAULT;
8580 player->special_action_bored = ACTION_DEFAULT;
8581 player->special_action_sleeping = ACTION_DEFAULT;
8586 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8589 static byte stored_player_action[MAX_PLAYERS];
8590 static int num_stored_actions = 0;
8592 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8593 int left = player_action & JOY_LEFT;
8594 int right = player_action & JOY_RIGHT;
8595 int up = player_action & JOY_UP;
8596 int down = player_action & JOY_DOWN;
8597 int button1 = player_action & JOY_BUTTON_1;
8598 int button2 = player_action & JOY_BUTTON_2;
8599 int dx = (left ? -1 : right ? 1 : 0);
8600 int dy = (up ? -1 : down ? 1 : 0);
8603 stored_player_action[player->index_nr] = 0;
8604 num_stored_actions++;
8608 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8611 if (!player->active || tape.pausing)
8615 printf("::: [%d %d %d %d] [%d %d]\n",
8616 left, right, up, down, button1, button2);
8622 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8627 if (player->MovPos == 0)
8628 CheckGravityMovement(player);
8631 snapped = SnapField(player, dx, dy);
8635 dropped = DropElement(player);
8637 moved = MovePlayer(player, dx, dy);
8640 if (tape.single_step && tape.recording && !tape.pausing)
8642 if (button1 || (dropped && !moved))
8644 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8645 SnapField(player, 0, 0); /* stop snapping */
8649 SetPlayerWaiting(player, FALSE);
8652 return player_action;
8654 stored_player_action[player->index_nr] = player_action;
8660 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8663 /* no actions for this player (no input at player's configured device) */
8665 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8666 SnapField(player, 0, 0);
8667 CheckGravityMovementWhenNotMoving(player);
8669 if (player->MovPos == 0)
8670 SetPlayerWaiting(player, TRUE);
8672 if (player->MovPos == 0) /* needed for tape.playing */
8673 player->is_moving = FALSE;
8675 player->is_dropping = FALSE;
8681 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8683 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8685 TapeRecordAction(stored_player_action);
8686 num_stored_actions = 0;
8693 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8695 static byte stored_player_action[MAX_PLAYERS];
8696 static int num_stored_actions = 0;
8697 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8698 int left = player_action & JOY_LEFT;
8699 int right = player_action & JOY_RIGHT;
8700 int up = player_action & JOY_UP;
8701 int down = player_action & JOY_DOWN;
8702 int button1 = player_action & JOY_BUTTON_1;
8703 int button2 = player_action & JOY_BUTTON_2;
8704 int dx = (left ? -1 : right ? 1 : 0);
8705 int dy = (up ? -1 : down ? 1 : 0);
8707 stored_player_action[player->index_nr] = 0;
8708 num_stored_actions++;
8710 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8712 if (!player->active || tape.pausing)
8717 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8720 snapped = SnapField(player, dx, dy);
8724 dropped = DropElement(player);
8726 moved = MovePlayer(player, dx, dy);
8729 if (tape.single_step && tape.recording && !tape.pausing)
8731 if (button1 || (dropped && !moved))
8733 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8734 SnapField(player, 0, 0); /* stop snapping */
8738 stored_player_action[player->index_nr] = player_action;
8742 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8744 /* no actions for this player (no input at player's configured device) */
8746 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8747 SnapField(player, 0, 0);
8748 CheckGravityMovementWhenNotMoving(player);
8750 if (player->MovPos == 0)
8751 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8753 if (player->MovPos == 0) /* needed for tape.playing */
8754 player->is_moving = FALSE;
8757 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8759 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8761 TapeRecordAction(stored_player_action);
8762 num_stored_actions = 0;
8767 void AdvanceFrameAndPlayerCounters(int player_nr)
8771 /* advance frame counters (global frame counter and time frame counter) */
8775 /* advance player counters (counters for move delay, move animation etc.) */
8776 for (i = 0; i < MAX_PLAYERS; i++)
8778 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8780 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8782 if (!advance_player_counters) /* not all players may be affected */
8785 stored_player[i].Frame += move_frames;
8787 if (stored_player[i].MovPos != 0)
8788 stored_player[i].StepFrame += move_frames;
8790 #if USE_NEW_MOVE_DELAY
8791 if (stored_player[i].move_delay > 0)
8792 stored_player[i].move_delay--;
8795 #if USE_NEW_PUSH_DELAY
8796 /* due to bugs in previous versions, counter must count up, not down */
8797 if (stored_player[i].push_delay != -1)
8798 stored_player[i].push_delay++;
8801 if (stored_player[i].drop_delay > 0)
8802 stored_player[i].drop_delay--;
8808 static unsigned long game_frame_delay = 0;
8809 unsigned long game_frame_delay_value;
8810 int magic_wall_x = 0, magic_wall_y = 0;
8811 int i, x, y, element, graphic;
8812 byte *recorded_player_action;
8813 byte summarized_player_action = 0;
8815 byte tape_action[MAX_PLAYERS];
8818 if (game_status != GAME_MODE_PLAYING)
8821 game_frame_delay_value =
8822 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8824 if (tape.playing && tape.warp_forward && !tape.pausing)
8825 game_frame_delay_value = 0;
8827 /* ---------- main game synchronization point ---------- */
8829 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8831 if (network_playing && !network_player_action_received)
8835 printf("DEBUG: try to get network player actions in time\n");
8839 #if defined(NETWORK_AVALIABLE)
8840 /* last chance to get network player actions without main loop delay */
8844 if (game_status != GAME_MODE_PLAYING)
8847 if (!network_player_action_received)
8851 printf("DEBUG: failed to get network player actions in time\n");
8862 printf("::: getting new tape action [%d]\n", FrameCounter);
8865 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8868 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8869 if (recorded_player_action == NULL && tape.pausing)
8874 printf("::: %d\n", stored_player[0].action);
8878 if (recorded_player_action != NULL)
8879 for (i = 0; i < MAX_PLAYERS; i++)
8880 stored_player[i].action = recorded_player_action[i];
8883 for (i = 0; i < MAX_PLAYERS; i++)
8885 summarized_player_action |= stored_player[i].action;
8887 if (!network_playing)
8888 stored_player[i].effective_action = stored_player[i].action;
8891 #if defined(NETWORK_AVALIABLE)
8892 if (network_playing)
8893 SendToServer_MovePlayer(summarized_player_action);
8896 if (!options.network && !setup.team_mode)
8897 local_player->effective_action = summarized_player_action;
8900 if (recorded_player_action != NULL)
8901 for (i = 0; i < MAX_PLAYERS; i++)
8902 stored_player[i].effective_action = recorded_player_action[i];
8906 for (i = 0; i < MAX_PLAYERS; i++)
8908 tape_action[i] = stored_player[i].effective_action;
8910 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8911 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8914 /* only save actions from input devices, but not programmed actions */
8916 TapeRecordAction(tape_action);
8919 for (i = 0; i < MAX_PLAYERS; i++)
8921 int actual_player_action = stored_player[i].effective_action;
8924 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8925 - rnd_equinox_tetrachloride 048
8926 - rnd_equinox_tetrachloride_ii 096
8927 - rnd_emanuel_schmieg 002
8928 - doctor_sloan_ww 001, 020
8930 if (stored_player[i].MovPos == 0)
8931 CheckGravityMovement(&stored_player[i]);
8935 /* overwrite programmed action with tape action */
8936 if (stored_player[i].programmed_action)
8937 actual_player_action = stored_player[i].programmed_action;
8941 if (stored_player[i].programmed_action)
8942 printf("::: %d\n", stored_player[i].programmed_action);
8945 if (recorded_player_action)
8948 if (stored_player[i].programmed_action &&
8949 stored_player[i].programmed_action != recorded_player_action[i])
8950 printf("::: %d: %d <-> %d\n", i,
8951 stored_player[i].programmed_action, recorded_player_action[i]);
8955 actual_player_action = recorded_player_action[i];
8960 /* overwrite tape action with programmed action */
8961 if (stored_player[i].programmed_action)
8962 actual_player_action = stored_player[i].programmed_action;
8967 printf("::: action: %d: %x [%d]\n",
8968 stored_player[i].MovPos, actual_player_action, FrameCounter);
8972 PlayerActions(&stored_player[i], actual_player_action);
8974 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8976 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8977 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8980 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8985 TapeRecordAction(tape_action);
8988 network_player_action_received = FALSE;
8990 ScrollScreen(NULL, SCROLL_GO_ON);
8996 for (i = 0; i < MAX_PLAYERS; i++)
8997 stored_player[i].Frame++;
9001 /* for backwards compatibility, the following code emulates a fixed bug that
9002 occured when pushing elements (causing elements that just made their last
9003 pushing step to already (if possible) make their first falling step in the
9004 same game frame, which is bad); this code is also needed to use the famous
9005 "spring push bug" which is used in older levels and might be wanted to be
9006 used also in newer levels, but in this case the buggy pushing code is only
9007 affecting the "spring" element and no other elements */
9010 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9012 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9015 for (i = 0; i < MAX_PLAYERS; i++)
9017 struct PlayerInfo *player = &stored_player[i];
9022 if (player->active && player->is_pushing && player->is_moving &&
9024 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9025 Feld[x][y] == EL_SPRING))
9027 if (player->active && player->is_pushing && player->is_moving &&
9031 ContinueMoving(x, y);
9033 /* continue moving after pushing (this is actually a bug) */
9034 if (!IS_MOVING(x, y))
9043 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9045 Changed[x][y] = FALSE;
9046 ChangeEvent[x][y] = -1;
9048 #if USE_NEW_BLOCK_STYLE
9049 /* this must be handled before main playfield loop */
9050 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9053 if (MovDelay[x][y] <= 0)
9059 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9061 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9062 printf("GameActions(): This should never happen!\n");
9064 ChangePage[x][y] = -1;
9069 if (WasJustMoving[x][y] > 0)
9070 WasJustMoving[x][y]--;
9071 if (WasJustFalling[x][y] > 0)
9072 WasJustFalling[x][y]--;
9073 if (CheckCollision[x][y] > 0)
9074 CheckCollision[x][y]--;
9079 /* reset finished pushing action (not done in ContinueMoving() to allow
9080 continous pushing animation for elements with zero push delay) */
9081 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9083 ResetGfxAnimation(x, y);
9084 DrawLevelField(x, y);
9089 if (IS_BLOCKED(x, y))
9093 Blocked2Moving(x, y, &oldx, &oldy);
9094 if (!IS_MOVING(oldx, oldy))
9096 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9097 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9098 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9099 printf("GameActions(): This should never happen!\n");
9105 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9107 element = Feld[x][y];
9109 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9111 graphic = el2img(element);
9117 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9119 element = graphic = 0;
9123 if (graphic_info[graphic].anim_global_sync)
9124 GfxFrame[x][y] = FrameCounter;
9126 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9127 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9128 ResetRandomAnimationValue(x, y);
9130 SetRandomAnimationValue(x, y);
9133 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9136 if (IS_INACTIVE(element))
9138 if (IS_ANIMATED(graphic))
9139 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9145 /* this may take place after moving, so 'element' may have changed */
9147 if (IS_CHANGING(x, y))
9149 if (IS_CHANGING(x, y) &&
9150 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9154 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9155 element_info[element].event_page_nr[CE_DELAY]);
9157 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9160 element = Feld[x][y];
9161 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9165 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9170 element = Feld[x][y];
9171 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9173 if (element == EL_MOLE)
9174 printf("::: %d, %d, %d [%d]\n",
9175 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9179 if (element == EL_YAMYAM)
9180 printf("::: %d, %d, %d\n",
9181 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9185 if (IS_ANIMATED(graphic) &&
9189 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9192 if (element == EL_BUG)
9193 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9197 if (element == EL_MOLE)
9198 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9202 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9203 EdelsteinFunkeln(x, y);
9205 else if ((element == EL_ACID ||
9206 element == EL_EXIT_OPEN ||
9207 element == EL_SP_EXIT_OPEN ||
9208 element == EL_SP_TERMINAL ||
9209 element == EL_SP_TERMINAL_ACTIVE ||
9210 element == EL_EXTRA_TIME ||
9211 element == EL_SHIELD_NORMAL ||
9212 element == EL_SHIELD_DEADLY) &&
9213 IS_ANIMATED(graphic))
9214 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9215 else if (IS_MOVING(x, y))
9216 ContinueMoving(x, y);
9217 else if (IS_ACTIVE_BOMB(element))
9218 CheckDynamite(x, y);
9220 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9221 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9223 else if (element == EL_AMOEBA_GROWING)
9224 AmoebeWaechst(x, y);
9225 else if (element == EL_AMOEBA_SHRINKING)
9226 AmoebaDisappearing(x, y);
9228 #if !USE_NEW_AMOEBA_CODE
9229 else if (IS_AMOEBALIVE(element))
9230 AmoebeAbleger(x, y);
9233 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9235 else if (element == EL_EXIT_CLOSED)
9237 else if (element == EL_SP_EXIT_CLOSED)
9239 else if (element == EL_EXPANDABLE_WALL_GROWING)
9241 else if (element == EL_EXPANDABLE_WALL ||
9242 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9243 element == EL_EXPANDABLE_WALL_VERTICAL ||
9244 element == EL_EXPANDABLE_WALL_ANY)
9246 else if (element == EL_FLAMES)
9247 CheckForDragon(x, y);
9249 else if (IS_AUTO_CHANGING(element))
9250 ChangeElement(x, y);
9252 else if (element == EL_EXPLOSION)
9253 ; /* drawing of correct explosion animation is handled separately */
9254 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9255 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9258 /* this may take place after moving, so 'element' may have changed */
9259 if (IS_AUTO_CHANGING(Feld[x][y]))
9260 ChangeElement(x, y);
9263 if (IS_BELT_ACTIVE(element))
9264 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9266 if (game.magic_wall_active)
9268 int jx = local_player->jx, jy = local_player->jy;
9270 /* play the element sound at the position nearest to the player */
9271 if ((element == EL_MAGIC_WALL_FULL ||
9272 element == EL_MAGIC_WALL_ACTIVE ||
9273 element == EL_MAGIC_WALL_EMPTYING ||
9274 element == EL_BD_MAGIC_WALL_FULL ||
9275 element == EL_BD_MAGIC_WALL_ACTIVE ||
9276 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9277 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9285 #if USE_NEW_AMOEBA_CODE
9286 /* new experimental amoeba growth stuff */
9288 if (!(FrameCounter % 8))
9291 static unsigned long random = 1684108901;
9293 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9296 x = (random >> 10) % lev_fieldx;
9297 y = (random >> 20) % lev_fieldy;
9299 x = RND(lev_fieldx);
9300 y = RND(lev_fieldy);
9302 element = Feld[x][y];
9305 if (!IS_PLAYER(x,y) &&
9306 (element == EL_EMPTY ||
9307 CAN_GROW_INTO(element) ||
9308 element == EL_QUICKSAND_EMPTY ||
9309 element == EL_ACID_SPLASH_LEFT ||
9310 element == EL_ACID_SPLASH_RIGHT))
9312 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9313 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9314 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9315 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9316 Feld[x][y] = EL_AMOEBA_DROP;
9319 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9320 if (!IS_PLAYER(x,y) &&
9321 (element == EL_EMPTY ||
9322 element == EL_SAND ||
9323 element == EL_QUICKSAND_EMPTY ||
9324 element == EL_ACID_SPLASH_LEFT ||
9325 element == EL_ACID_SPLASH_RIGHT))
9327 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9328 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9329 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9330 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9331 Feld[x][y] = EL_AMOEBA_DROP;
9335 random = random * 129 + 1;
9341 if (game.explosions_delayed)
9344 game.explosions_delayed = FALSE;
9346 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9348 element = Feld[x][y];
9350 if (ExplodeField[x][y])
9351 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9352 else if (element == EL_EXPLOSION)
9353 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9355 ExplodeField[x][y] = EX_TYPE_NONE;
9358 game.explosions_delayed = TRUE;
9361 if (game.magic_wall_active)
9363 if (!(game.magic_wall_time_left % 4))
9365 int element = Feld[magic_wall_x][magic_wall_y];
9367 if (element == EL_BD_MAGIC_WALL_FULL ||
9368 element == EL_BD_MAGIC_WALL_ACTIVE ||
9369 element == EL_BD_MAGIC_WALL_EMPTYING)
9370 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9372 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9375 if (game.magic_wall_time_left > 0)
9377 game.magic_wall_time_left--;
9378 if (!game.magic_wall_time_left)
9380 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9382 element = Feld[x][y];
9384 if (element == EL_MAGIC_WALL_ACTIVE ||
9385 element == EL_MAGIC_WALL_FULL)
9387 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9388 DrawLevelField(x, y);
9390 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9391 element == EL_BD_MAGIC_WALL_FULL)
9393 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9394 DrawLevelField(x, y);
9398 game.magic_wall_active = FALSE;
9403 if (game.light_time_left > 0)
9405 game.light_time_left--;
9407 if (game.light_time_left == 0)
9408 RedrawAllLightSwitchesAndInvisibleElements();
9411 if (game.timegate_time_left > 0)
9413 game.timegate_time_left--;
9415 if (game.timegate_time_left == 0)
9416 CloseAllOpenTimegates();
9419 for (i = 0; i < MAX_PLAYERS; i++)
9421 struct PlayerInfo *player = &stored_player[i];
9423 if (SHIELD_ON(player))
9425 if (player->shield_deadly_time_left)
9426 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9427 else if (player->shield_normal_time_left)
9428 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9432 if (TimeFrames >= FRAMES_PER_SECOND)
9437 for (i = 0; i < MAX_PLAYERS; i++)
9439 struct PlayerInfo *player = &stored_player[i];
9441 if (SHIELD_ON(player))
9443 player->shield_normal_time_left--;
9445 if (player->shield_deadly_time_left > 0)
9446 player->shield_deadly_time_left--;
9450 if (!level.use_step_counter)
9458 if (TimeLeft <= 10 && setup.time_limit)
9459 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9461 DrawGameValue_Time(TimeLeft);
9463 if (!TimeLeft && setup.time_limit)
9464 for (i = 0; i < MAX_PLAYERS; i++)
9465 KillHero(&stored_player[i]);
9467 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9468 DrawGameValue_Time(TimePlayed);
9471 if (tape.recording || tape.playing)
9472 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9476 PlayAllPlayersSound();
9478 if (options.debug) /* calculate frames per second */
9480 static unsigned long fps_counter = 0;
9481 static int fps_frames = 0;
9482 unsigned long fps_delay_ms = Counter() - fps_counter;
9486 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9488 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9491 fps_counter = Counter();
9494 redraw_mask |= REDRAW_FPS;
9498 if (stored_player[0].jx != stored_player[0].last_jx ||
9499 stored_player[0].jy != stored_player[0].last_jy)
9500 printf("::: %d, %d, %d, %d, %d\n",
9501 stored_player[0].MovDir,
9502 stored_player[0].MovPos,
9503 stored_player[0].GfxPos,
9504 stored_player[0].Frame,
9505 stored_player[0].StepFrame);
9508 #if USE_NEW_MOVE_DELAY
9509 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9514 for (i = 0; i < MAX_PLAYERS; i++)
9517 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9519 stored_player[i].Frame += move_frames;
9521 if (stored_player[i].MovPos != 0)
9522 stored_player[i].StepFrame += move_frames;
9524 #if USE_NEW_MOVE_DELAY
9525 if (stored_player[i].move_delay > 0)
9526 stored_player[i].move_delay--;
9529 if (stored_player[i].drop_delay > 0)
9530 stored_player[i].drop_delay--;
9535 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9537 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9539 local_player->show_envelope = 0;
9543 #if USE_NEW_RANDOMIZE
9544 /* use random number generator in every frame to make it less predictable */
9545 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9550 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9552 int min_x = x, min_y = y, max_x = x, max_y = y;
9555 for (i = 0; i < MAX_PLAYERS; i++)
9557 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9559 if (!stored_player[i].active || &stored_player[i] == player)
9562 min_x = MIN(min_x, jx);
9563 min_y = MIN(min_y, jy);
9564 max_x = MAX(max_x, jx);
9565 max_y = MAX(max_y, jy);
9568 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9571 static boolean AllPlayersInVisibleScreen()
9575 for (i = 0; i < MAX_PLAYERS; i++)
9577 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9579 if (!stored_player[i].active)
9582 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9589 void ScrollLevel(int dx, int dy)
9591 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9594 BlitBitmap(drawto_field, drawto_field,
9595 FX + TILEX * (dx == -1) - softscroll_offset,
9596 FY + TILEY * (dy == -1) - softscroll_offset,
9597 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9598 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9599 FX + TILEX * (dx == 1) - softscroll_offset,
9600 FY + TILEY * (dy == 1) - softscroll_offset);
9604 x = (dx == 1 ? BX1 : BX2);
9605 for (y = BY1; y <= BY2; y++)
9606 DrawScreenField(x, y);
9611 y = (dy == 1 ? BY1 : BY2);
9612 for (x = BX1; x <= BX2; x++)
9613 DrawScreenField(x, y);
9616 redraw_mask |= REDRAW_FIELD;
9620 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9622 int nextx = x + dx, nexty = y + dy;
9623 int element = Feld[x][y];
9626 element != EL_SP_PORT_LEFT &&
9627 element != EL_SP_GRAVITY_PORT_LEFT &&
9628 element != EL_SP_PORT_HORIZONTAL &&
9629 element != EL_SP_PORT_ANY) ||
9631 element != EL_SP_PORT_RIGHT &&
9632 element != EL_SP_GRAVITY_PORT_RIGHT &&
9633 element != EL_SP_PORT_HORIZONTAL &&
9634 element != EL_SP_PORT_ANY) ||
9636 element != EL_SP_PORT_UP &&
9637 element != EL_SP_GRAVITY_PORT_UP &&
9638 element != EL_SP_PORT_VERTICAL &&
9639 element != EL_SP_PORT_ANY) ||
9641 element != EL_SP_PORT_DOWN &&
9642 element != EL_SP_GRAVITY_PORT_DOWN &&
9643 element != EL_SP_PORT_VERTICAL &&
9644 element != EL_SP_PORT_ANY) ||
9645 !IN_LEV_FIELD(nextx, nexty) ||
9646 !IS_FREE(nextx, nexty))
9653 static boolean canFallDown(struct PlayerInfo *player)
9655 int jx = player->jx, jy = player->jy;
9657 return (IN_LEV_FIELD(jx, jy + 1) &&
9658 (IS_FREE(jx, jy + 1) ||
9659 #if USE_NEW_BLOCK_STYLE
9660 #if USE_GRAVITY_BUGFIX_OLD
9661 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9664 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9665 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9666 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9669 static boolean canPassField(int x, int y, int move_dir)
9671 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9672 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9673 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9676 int element = Feld[x][y];
9678 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9679 !CAN_MOVE(element) &&
9680 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9681 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9682 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9685 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9687 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9688 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9689 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9693 int nextx = newx + dx;
9694 int nexty = newy + dy;
9698 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9699 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9701 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9703 (IS_DIGGABLE(Feld[newx][newy]) ||
9704 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9705 canPassField(newx, newy, move_dir)));
9708 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9709 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9710 (IS_DIGGABLE(Feld[newx][newy]) ||
9711 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9712 canPassField(newx, newy, move_dir)));
9715 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9716 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9717 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9718 canPassField(newx, newy, move_dir)));
9720 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9721 (IS_DIGGABLE(Feld[newx][newy]) ||
9722 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9723 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9724 !CAN_MOVE(Feld[newx][newy]) &&
9725 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9726 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9727 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9733 static void CheckGravityMovement(struct PlayerInfo *player)
9735 if (game.gravity && !player->programmed_action)
9738 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9739 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9741 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9742 int move_dir_vertical = player->action & MV_VERTICAL;
9746 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9748 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9751 int jx = player->jx, jy = player->jy;
9753 boolean player_is_moving_to_valid_field =
9754 (!player_is_snapping &&
9755 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9756 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9760 (player->last_move_dir & MV_HORIZONTAL ?
9761 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9762 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9766 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9767 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9768 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9769 int new_jx = jx + dx, new_jy = jy + dy;
9770 int nextx = new_jx + dx, nexty = new_jy + dy;
9776 boolean player_can_fall_down = canFallDown(player);
9778 boolean player_can_fall_down =
9779 (IN_LEV_FIELD(jx, jy + 1) &&
9780 (IS_FREE(jx, jy + 1) ||
9781 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9785 boolean player_can_fall_down =
9786 (IN_LEV_FIELD(jx, jy + 1) &&
9787 (IS_FREE(jx, jy + 1)));
9791 boolean player_is_moving_to_valid_field =
9794 !player_is_snapping &&
9798 IN_LEV_FIELD(new_jx, new_jy) &&
9799 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9800 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9801 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9802 IN_LEV_FIELD(nextx, nexty) &&
9803 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9805 IN_LEV_FIELD(new_jx, new_jy) &&
9806 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9807 Feld[new_jx][new_jy] == EL_SAND ||
9808 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9809 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9810 /* !!! extend EL_SAND to anything diggable !!! */
9816 boolean player_is_standing_on_valid_field =
9817 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9818 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9822 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9823 player_can_fall_down,
9824 player_is_standing_on_valid_field,
9825 player_is_moving_to_valid_field,
9826 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9827 player->effective_action,
9828 player->can_fall_into_acid);
9831 if (player_can_fall_down &&
9833 !player_is_standing_on_valid_field &&
9835 !player_is_moving_to_valid_field)
9838 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9839 jx, jy, FrameCounter);
9842 player->programmed_action = MV_DOWN;
9847 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9850 return CheckGravityMovement(player);
9853 if (game.gravity && !player->programmed_action)
9855 int jx = player->jx, jy = player->jy;
9856 boolean field_under_player_is_free =
9857 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9858 boolean player_is_standing_on_valid_field =
9859 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9860 (IS_WALKABLE(Feld[jx][jy]) &&
9861 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9863 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9864 player->programmed_action = MV_DOWN;
9870 -----------------------------------------------------------------------------
9871 dx, dy: direction (non-diagonal) to try to move the player to
9872 real_dx, real_dy: direction as read from input device (can be diagonal)
9875 boolean MovePlayerOneStep(struct PlayerInfo *player,
9876 int dx, int dy, int real_dx, int real_dy)
9879 static int trigger_sides[4][2] =
9881 /* enter side leave side */
9882 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9883 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9884 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9885 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9887 int move_direction = (dx == -1 ? MV_LEFT :
9888 dx == +1 ? MV_RIGHT :
9890 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9891 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9892 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9894 int jx = player->jx, jy = player->jy;
9895 int new_jx = jx + dx, new_jy = jy + dy;
9899 if (!player->active || (!dx && !dy))
9900 return MF_NO_ACTION;
9902 player->MovDir = (dx < 0 ? MV_LEFT :
9905 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9907 if (!IN_LEV_FIELD(new_jx, new_jy))
9908 return MF_NO_ACTION;
9910 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9911 return MF_NO_ACTION;
9914 element = MovingOrBlocked2Element(new_jx, new_jy);
9916 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9919 if (DONT_RUN_INTO(element))
9921 if (element == EL_ACID && dx == 0 && dy == 1)
9923 SplashAcid(new_jx, new_jy);
9924 Feld[jx][jy] = EL_PLAYER_1;
9925 InitMovingField(jx, jy, MV_DOWN);
9926 Store[jx][jy] = EL_ACID;
9927 ContinueMoving(jx, jy);
9931 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9936 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9937 if (can_move != MF_MOVING)
9940 /* check if DigField() has caused relocation of the player */
9941 if (player->jx != jx || player->jy != jy)
9942 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9944 StorePlayer[jx][jy] = 0;
9945 player->last_jx = jx;
9946 player->last_jy = jy;
9947 player->jx = new_jx;
9948 player->jy = new_jy;
9949 StorePlayer[new_jx][new_jy] = player->element_nr;
9952 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9954 player->step_counter++;
9957 player->drop_delay = 0;
9960 PlayerVisit[jx][jy] = FrameCounter;
9962 ScrollPlayer(player, SCROLL_INIT);
9965 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9967 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
9969 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9972 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9974 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9975 CE_PLAYER_ENTERS_X, enter_side);
9976 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9977 CE_ENTERED_BY_PLAYER, enter_side);
9984 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9986 int jx = player->jx, jy = player->jy;
9987 int old_jx = jx, old_jy = jy;
9988 int moved = MF_NO_ACTION;
9991 if (!player->active)
9996 if (player->MovPos == 0)
9998 player->is_moving = FALSE;
9999 player->is_digging = FALSE;
10000 player->is_collecting = FALSE;
10001 player->is_snapping = FALSE;
10002 player->is_pushing = FALSE;
10008 if (!player->active || (!dx && !dy))
10013 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10021 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
10022 player->move_delay + player->move_delay_value);
10025 #if USE_NEW_MOVE_DELAY
10026 if (player->move_delay > 0)
10028 if (!FrameReached(&player->move_delay, player->move_delay_value))
10032 printf("::: can NOT move\n");
10038 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10039 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10046 printf("::: COULD move now\n");
10049 #if USE_NEW_MOVE_DELAY
10050 player->move_delay = -1; /* set to "uninitialized" value */
10053 /* store if player is automatically moved to next field */
10054 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10056 /* remove the last programmed player action */
10057 player->programmed_action = 0;
10059 if (player->MovPos)
10061 /* should only happen if pre-1.2 tape recordings are played */
10062 /* this is only for backward compatibility */
10064 int original_move_delay_value = player->move_delay_value;
10067 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10071 /* scroll remaining steps with finest movement resolution */
10072 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10074 while (player->MovPos)
10076 ScrollPlayer(player, SCROLL_GO_ON);
10077 ScrollScreen(NULL, SCROLL_GO_ON);
10079 #if USE_NEW_MOVE_DELAY
10080 AdvanceFrameAndPlayerCounters(player->index_nr);
10089 player->move_delay_value = original_move_delay_value;
10092 if (player->last_move_dir & MV_HORIZONTAL)
10094 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10095 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10099 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10100 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10106 if (moved & MF_MOVING && !ScreenMovPos &&
10107 (player == local_player || !options.network))
10109 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10110 int offset = (setup.scroll_delay ? 3 : 0);
10112 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10114 /* actual player has left the screen -- scroll in that direction */
10115 if (jx != old_jx) /* player has moved horizontally */
10116 scroll_x += (jx - old_jx);
10117 else /* player has moved vertically */
10118 scroll_y += (jy - old_jy);
10122 if (jx != old_jx) /* player has moved horizontally */
10124 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10125 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10126 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10128 /* don't scroll over playfield boundaries */
10129 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10130 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10132 /* don't scroll more than one field at a time */
10133 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10135 /* don't scroll against the player's moving direction */
10136 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10137 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10138 scroll_x = old_scroll_x;
10140 else /* player has moved vertically */
10142 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10143 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10144 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10146 /* don't scroll over playfield boundaries */
10147 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10148 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10150 /* don't scroll more than one field at a time */
10151 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10153 /* don't scroll against the player's moving direction */
10154 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10155 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10156 scroll_y = old_scroll_y;
10160 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10162 if (!options.network && !AllPlayersInVisibleScreen())
10164 scroll_x = old_scroll_x;
10165 scroll_y = old_scroll_y;
10169 ScrollScreen(player, SCROLL_INIT);
10170 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10177 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10179 if (!(moved & MF_MOVING) && !player->is_pushing)
10184 player->StepFrame = 0;
10186 if (moved & MF_MOVING)
10189 printf("::: REALLY moves now\n");
10192 if (old_jx != jx && old_jy == jy)
10193 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10194 else if (old_jx == jx && old_jy != jy)
10195 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10197 DrawLevelField(jx, jy); /* for "crumbled sand" */
10199 player->last_move_dir = player->MovDir;
10200 player->is_moving = TRUE;
10202 player->is_snapping = FALSE;
10206 player->is_switching = FALSE;
10209 player->is_dropping = FALSE;
10213 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10216 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10219 int move_direction = player->MovDir;
10221 int enter_side = MV_DIR_OPPOSITE(move_direction);
10222 int leave_side = move_direction;
10224 static int trigger_sides[4][2] =
10226 /* enter side leave side */
10227 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10228 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10229 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10230 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10232 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10233 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10235 int old_element = Feld[old_jx][old_jy];
10236 int new_element = Feld[jx][jy];
10239 /* !!! TEST ONLY !!! */
10240 if (IS_CUSTOM_ELEMENT(old_element))
10241 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10243 player->index_bit, leave_side);
10245 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10246 CE_PLAYER_LEAVES_X,
10247 player->index_bit, leave_side);
10249 if (IS_CUSTOM_ELEMENT(new_element))
10250 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10251 player->index_bit, enter_side);
10253 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10254 CE_PLAYER_ENTERS_X,
10255 player->index_bit, enter_side);
10265 CheckGravityMovementWhenNotMoving(player);
10268 player->last_move_dir = MV_NO_MOVING;
10270 player->is_moving = FALSE;
10272 #if USE_NEW_MOVE_STYLE
10273 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10274 /* ensure that the player is also allowed to move in the next frame */
10275 /* (currently, the player is forced to wait eight frames before he can try
10278 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10279 player->move_delay = 0; /* allow direct movement in the next frame */
10283 #if USE_NEW_MOVE_DELAY
10284 if (player->move_delay == -1) /* not yet initialized by DigField() */
10285 player->move_delay = player->move_delay_value;
10288 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10290 TestIfHeroTouchesBadThing(jx, jy);
10291 TestIfPlayerTouchesCustomElement(jx, jy);
10294 if (!player->active)
10295 RemoveHero(player);
10300 void ScrollPlayer(struct PlayerInfo *player, int mode)
10302 int jx = player->jx, jy = player->jy;
10303 int last_jx = player->last_jx, last_jy = player->last_jy;
10304 int move_stepsize = TILEX / player->move_delay_value;
10306 if (!player->active || !player->MovPos)
10309 if (mode == SCROLL_INIT)
10311 player->actual_frame_counter = FrameCounter;
10312 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10315 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10317 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10318 player->block_delay);
10321 #if USE_NEW_BLOCK_STYLE
10324 if (player->block_delay <= 0)
10325 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10328 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10329 Feld[last_jx][last_jy] == EL_EMPTY)
10331 int last_field_block_delay = 0; /* start with no blocking at all */
10332 int block_delay_adjustment = player->block_delay_adjustment;
10334 /* if player blocks last field, add delay for exactly one move */
10335 if (player->block_last_field)
10337 last_field_block_delay += player->move_delay_value;
10339 #if USE_GRAVITY_BUGFIX_NEW
10340 /* when blocking enabled, prevent moving up despite gravity */
10341 if (game.gravity && player->MovDir == MV_UP)
10342 block_delay_adjustment = -1;
10346 /* add block delay adjustment (also possible when not blocking) */
10347 last_field_block_delay += block_delay_adjustment;
10350 #if USE_BLOCK_DELAY_BUGFIX
10351 /* when blocking enabled, correct block delay for fast movement */
10352 if (player->block_last_field &&
10353 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10354 last_field_block_delay =
10355 player->move_delay_value + player->block_delay_adjustment;
10360 #if USE_GRAVITY_BUGFIX_NEW
10361 /* when blocking enabled, correct block delay for gravity movement */
10362 if (player->block_last_field &&
10363 game.gravity && player->MovDir == MV_UP)
10364 last_field_block_delay = player->move_delay_value - 1;
10368 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10369 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10372 #if USE_NEW_MOVE_STYLE
10373 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10374 player->block_last_field) &&
10375 Feld[last_jx][last_jy] == EL_EMPTY)
10376 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10378 if (Feld[last_jx][last_jy] == EL_EMPTY)
10379 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10384 DrawPlayer(player);
10389 else if (!FrameReached(&player->actual_frame_counter, 1))
10392 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10393 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10395 #if USE_NEW_BLOCK_STYLE
10397 if (!player->block_last_field &&
10398 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10400 RemoveField(last_jx, last_jy);
10402 Feld[last_jx][last_jy] = EL_EMPTY;
10406 /* before DrawPlayer() to draw correct player graphic for this case */
10407 if (player->MovPos == 0)
10408 CheckGravityMovement(player);
10411 DrawPlayer(player); /* needed here only to cleanup last field */
10414 if (player->MovPos == 0) /* player reached destination field */
10417 if (player->move_delay_reset_counter > 0)
10419 player->move_delay_reset_counter--;
10421 if (player->move_delay_reset_counter == 0)
10423 /* continue with normal speed after quickly moving through gate */
10424 HALVE_PLAYER_SPEED(player);
10426 /* be able to make the next move without delay */
10427 player->move_delay = 0;
10431 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10433 /* continue with normal speed after quickly moving through gate */
10434 HALVE_PLAYER_SPEED(player);
10436 /* be able to make the next move without delay */
10437 player->move_delay = 0;
10441 #if USE_NEW_BLOCK_STYLE
10443 if (player->block_last_field &&
10444 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10446 RemoveField(last_jx, last_jy);
10448 Feld[last_jx][last_jy] = EL_EMPTY;
10452 player->last_jx = jx;
10453 player->last_jy = jy;
10455 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10456 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10457 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10459 DrawPlayer(player); /* needed here only to cleanup last field */
10460 RemoveHero(player);
10462 if (local_player->friends_still_needed == 0 ||
10463 IS_SP_ELEMENT(Feld[jx][jy]))
10464 player->LevelSolved = player->GameOver = TRUE;
10468 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10469 /* this breaks one level: "machine", level 000 */
10471 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10474 int move_direction = player->MovDir;
10476 int enter_side = MV_DIR_OPPOSITE(move_direction);
10477 int leave_side = move_direction;
10479 static int trigger_sides[4][2] =
10481 /* enter side leave side */
10482 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10483 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10484 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10485 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10487 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10488 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10490 int old_jx = last_jx;
10491 int old_jy = last_jy;
10492 int old_element = Feld[old_jx][old_jy];
10493 int new_element = Feld[jx][jy];
10496 /* !!! TEST ONLY !!! */
10497 if (IS_CUSTOM_ELEMENT(old_element))
10498 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10500 player->index_bit, leave_side);
10502 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10503 CE_PLAYER_LEAVES_X,
10504 player->index_bit, leave_side);
10506 if (IS_CUSTOM_ELEMENT(new_element))
10507 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10508 player->index_bit, enter_side);
10510 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10511 CE_PLAYER_ENTERS_X,
10512 player->index_bit, enter_side);
10518 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10520 TestIfHeroTouchesBadThing(jx, jy);
10521 TestIfPlayerTouchesCustomElement(jx, jy);
10524 /* needed because pushed element has not yet reached its destination,
10525 so it would trigger a change event at its previous field location */
10526 if (!player->is_pushing)
10528 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10531 if (!player->active)
10532 RemoveHero(player);
10535 if (level.use_step_counter)
10545 if (TimeLeft <= 10 && setup.time_limit)
10546 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10548 DrawGameValue_Time(TimeLeft);
10550 if (!TimeLeft && setup.time_limit)
10551 for (i = 0; i < MAX_PLAYERS; i++)
10552 KillHero(&stored_player[i]);
10554 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10555 DrawGameValue_Time(TimePlayed);
10558 if (tape.single_step && tape.recording && !tape.pausing &&
10559 !player->programmed_action)
10560 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10564 void ScrollScreen(struct PlayerInfo *player, int mode)
10566 static unsigned long screen_frame_counter = 0;
10568 if (mode == SCROLL_INIT)
10570 /* set scrolling step size according to actual player's moving speed */
10571 ScrollStepSize = TILEX / player->move_delay_value;
10573 screen_frame_counter = FrameCounter;
10574 ScreenMovDir = player->MovDir;
10575 ScreenMovPos = player->MovPos;
10576 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10579 else if (!FrameReached(&screen_frame_counter, 1))
10584 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10585 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10586 redraw_mask |= REDRAW_FIELD;
10589 ScreenMovDir = MV_NO_MOVING;
10592 void TestIfPlayerTouchesCustomElement(int x, int y)
10594 static int xy[4][2] =
10601 static int trigger_sides[4][2] =
10603 /* center side border side */
10604 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10605 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10606 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10607 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10609 static int touch_dir[4] =
10611 MV_LEFT | MV_RIGHT,
10616 int center_element = Feld[x][y]; /* should always be non-moving! */
10619 for (i = 0; i < NUM_DIRECTIONS; i++)
10621 int xx = x + xy[i][0];
10622 int yy = y + xy[i][1];
10623 int center_side = trigger_sides[i][0];
10624 int border_side = trigger_sides[i][1];
10625 int border_element;
10627 if (!IN_LEV_FIELD(xx, yy))
10630 if (IS_PLAYER(x, y))
10632 struct PlayerInfo *player = PLAYERINFO(x, y);
10634 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10635 border_element = Feld[xx][yy]; /* may be moving! */
10636 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10637 border_element = Feld[xx][yy];
10638 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10639 border_element = MovingOrBlocked2Element(xx, yy);
10641 continue; /* center and border element do not touch */
10644 /* !!! TEST ONLY !!! */
10645 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10646 player->index_bit, border_side);
10647 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10648 CE_PLAYER_TOUCHES_X,
10649 player->index_bit, border_side);
10651 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10652 CE_PLAYER_TOUCHES_X,
10653 player->index_bit, border_side);
10654 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10655 player->index_bit, border_side);
10658 else if (IS_PLAYER(xx, yy))
10660 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10662 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10664 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10665 continue; /* center and border element do not touch */
10669 /* !!! TEST ONLY !!! */
10670 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10671 player->index_bit, center_side);
10672 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10673 CE_PLAYER_TOUCHES_X,
10674 player->index_bit, center_side);
10676 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10677 CE_PLAYER_TOUCHES_X,
10678 player->index_bit, center_side);
10679 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10680 player->index_bit, center_side);
10688 void TestIfElementTouchesCustomElement(int x, int y)
10690 static int xy[4][2] =
10697 static int trigger_sides[4][2] =
10699 /* center side border side */
10700 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10701 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10702 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10703 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10705 static int touch_dir[4] =
10707 MV_LEFT | MV_RIGHT,
10712 boolean change_center_element = FALSE;
10713 int center_element_change_page = 0;
10714 int center_element = Feld[x][y]; /* should always be non-moving! */
10715 int border_trigger_element = EL_UNDEFINED;
10718 for (i = 0; i < NUM_DIRECTIONS; i++)
10720 int xx = x + xy[i][0];
10721 int yy = y + xy[i][1];
10722 int center_side = trigger_sides[i][0];
10723 int border_side = trigger_sides[i][1];
10724 int border_element;
10726 if (!IN_LEV_FIELD(xx, yy))
10729 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10730 border_element = Feld[xx][yy]; /* may be moving! */
10731 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10732 border_element = Feld[xx][yy];
10733 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10734 border_element = MovingOrBlocked2Element(xx, yy);
10736 continue; /* center and border element do not touch */
10738 /* check for change of center element (but change it only once) */
10739 if (IS_CUSTOM_ELEMENT(center_element) &&
10740 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
10741 !change_center_element)
10743 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10745 struct ElementChangeInfo *change =
10746 &element_info[center_element].change_page[j];
10748 if (change->can_change &&
10749 change->has_event[CE_TOUCHING_X] &&
10750 change->trigger_side & border_side &&
10752 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10754 change->trigger_element == border_element
10758 change_center_element = TRUE;
10759 center_element_change_page = j;
10760 border_trigger_element = border_element;
10767 /* check for change of border element */
10768 if (IS_CUSTOM_ELEMENT(border_element) &&
10769 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
10771 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10773 struct ElementChangeInfo *change =
10774 &element_info[border_element].change_page[j];
10776 if (change->can_change &&
10777 change->has_event[CE_TOUCHING_X] &&
10778 change->trigger_side & center_side &&
10780 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10782 change->trigger_element == center_element
10787 printf("::: border_element %d, %d\n", x, y);
10790 CheckElementChangeByPage(xx, yy, border_element, center_element,
10798 if (change_center_element)
10801 printf("::: center_element %d, %d\n", x, y);
10804 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10805 CE_TOUCHING_X, center_element_change_page);
10809 void TestIfElementHitsCustomElement(int x, int y, int direction)
10811 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10812 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10813 int hitx = x + dx, hity = y + dy;
10814 int hitting_element = Feld[x][y];
10815 int touched_element;
10817 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10818 !IS_FREE(hitx, hity) &&
10819 (!IS_MOVING(hitx, hity) ||
10820 MovDir[hitx][hity] != direction ||
10821 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10824 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10828 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10832 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10833 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10835 #if !USE_HITTING_SOMETHING_BUGFIX
10836 /* "hitting something" is also true when hitting the playfield border */
10837 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10838 CE_HITTING_SOMETHING, direction);
10841 if (IN_LEV_FIELD(hitx, hity))
10843 int opposite_direction = MV_DIR_OPPOSITE(direction);
10844 int hitting_side = direction;
10845 int touched_side = opposite_direction;
10847 int touched_element = MovingOrBlocked2Element(hitx, hity);
10850 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10851 MovDir[hitx][hity] != direction ||
10852 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10861 #if !USE_HIT_BY_SOMETHING_BUGFIX
10862 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10863 CE_HIT_BY_SOMETHING, opposite_direction);
10866 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10867 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
10869 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10871 struct ElementChangeInfo *change =
10872 &element_info[hitting_element].change_page[i];
10874 if (change->can_change &&
10875 change->has_event[CE_HITTING_X] &&
10876 change->trigger_side & touched_side &&
10879 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10881 change->trigger_element == touched_element
10885 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10892 if (IS_CUSTOM_ELEMENT(touched_element) &&
10893 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
10895 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10897 struct ElementChangeInfo *change =
10898 &element_info[touched_element].change_page[i];
10900 if (change->can_change &&
10901 change->has_event[CE_HIT_BY_X] &&
10902 change->trigger_side & hitting_side &&
10904 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10906 change->trigger_element == hitting_element
10910 CheckElementChangeByPage(hitx, hity, touched_element,
10911 hitting_element, CE_HIT_BY_X, i);
10917 #if USE_HIT_BY_SOMETHING_BUGFIX
10918 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10919 CE_HIT_BY_SOMETHING, opposite_direction);
10924 #if USE_HITTING_SOMETHING_BUGFIX
10925 /* "hitting something" is also true when hitting the playfield border */
10926 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10927 CE_HITTING_SOMETHING, direction);
10932 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10934 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10935 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10936 int hitx = x + dx, hity = y + dy;
10937 int hitting_element = Feld[x][y];
10938 int touched_element;
10940 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10941 !IS_FREE(hitx, hity) &&
10942 (!IS_MOVING(hitx, hity) ||
10943 MovDir[hitx][hity] != direction ||
10944 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10947 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10951 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10955 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10956 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10958 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10959 EP_CAN_SMASH_EVERYTHING, direction);
10961 if (IN_LEV_FIELD(hitx, hity))
10963 int opposite_direction = MV_DIR_OPPOSITE(direction);
10964 int hitting_side = direction;
10965 int touched_side = opposite_direction;
10967 int touched_element = MovingOrBlocked2Element(hitx, hity);
10970 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10971 MovDir[hitx][hity] != direction ||
10972 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10981 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10982 CE_SMASHED_BY_SOMETHING, opposite_direction);
10984 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10985 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10987 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10989 struct ElementChangeInfo *change =
10990 &element_info[hitting_element].change_page[i];
10992 if (change->can_change &&
10993 change->has_event[CE_OTHER_IS_SMASHING] &&
10994 change->trigger_side & touched_side &&
10997 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10999 change->trigger_element == touched_element
11003 CheckElementChangeByPage(x, y, hitting_element, touched_element,
11004 CE_OTHER_IS_SMASHING, i);
11010 if (IS_CUSTOM_ELEMENT(touched_element) &&
11011 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
11013 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11015 struct ElementChangeInfo *change =
11016 &element_info[touched_element].change_page[i];
11018 if (change->can_change &&
11019 change->has_event[CE_OTHER_GETS_SMASHED] &&
11020 change->trigger_side & hitting_side &&
11022 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11024 change->trigger_element == hitting_element
11028 CheckElementChangeByPage(hitx, hity, touched_element,
11029 hitting_element, CE_OTHER_GETS_SMASHED,i);
11039 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11041 int i, kill_x = -1, kill_y = -1;
11042 int bad_element = -1;
11043 static int test_xy[4][2] =
11050 static int test_dir[4] =
11058 for (i = 0; i < NUM_DIRECTIONS; i++)
11060 int test_x, test_y, test_move_dir, test_element;
11062 test_x = good_x + test_xy[i][0];
11063 test_y = good_y + test_xy[i][1];
11065 if (!IN_LEV_FIELD(test_x, test_y))
11069 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11072 test_element = Feld[test_x][test_y];
11074 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11077 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11078 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11080 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11081 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11085 bad_element = test_element;
11091 if (kill_x != -1 || kill_y != -1)
11093 if (IS_PLAYER(good_x, good_y))
11095 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11098 if (player->shield_deadly_time_left > 0 &&
11099 !IS_INDESTRUCTIBLE(bad_element))
11100 Bang(kill_x, kill_y);
11101 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11104 if (player->shield_deadly_time_left > 0)
11105 Bang(kill_x, kill_y);
11106 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11111 Bang(good_x, good_y);
11115 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11117 int i, kill_x = -1, kill_y = -1;
11118 int bad_element = Feld[bad_x][bad_y];
11119 static int test_xy[4][2] =
11126 static int touch_dir[4] =
11128 MV_LEFT | MV_RIGHT,
11133 static int test_dir[4] =
11141 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11144 for (i = 0; i < NUM_DIRECTIONS; i++)
11146 int test_x, test_y, test_move_dir, test_element;
11148 test_x = bad_x + test_xy[i][0];
11149 test_y = bad_y + test_xy[i][1];
11150 if (!IN_LEV_FIELD(test_x, test_y))
11154 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11156 test_element = Feld[test_x][test_y];
11158 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11159 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11161 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11162 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11164 /* good thing is player or penguin that does not move away */
11165 if (IS_PLAYER(test_x, test_y))
11167 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11169 if (bad_element == EL_ROBOT && player->is_moving)
11170 continue; /* robot does not kill player if he is moving */
11172 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11174 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11175 continue; /* center and border element do not touch */
11182 else if (test_element == EL_PENGUIN)
11191 if (kill_x != -1 || kill_y != -1)
11193 if (IS_PLAYER(kill_x, kill_y))
11195 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11198 if (player->shield_deadly_time_left > 0 &&
11199 !IS_INDESTRUCTIBLE(bad_element))
11200 Bang(bad_x, bad_y);
11201 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11204 if (player->shield_deadly_time_left > 0)
11205 Bang(bad_x, bad_y);
11206 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11211 Bang(kill_x, kill_y);
11215 void TestIfHeroTouchesBadThing(int x, int y)
11217 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11220 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11222 TestIfGoodThingHitsBadThing(x, y, move_dir);
11225 void TestIfBadThingTouchesHero(int x, int y)
11227 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11230 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11232 TestIfBadThingHitsGoodThing(x, y, move_dir);
11235 void TestIfFriendTouchesBadThing(int x, int y)
11237 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11240 void TestIfBadThingTouchesFriend(int x, int y)
11242 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11245 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11247 int i, kill_x = bad_x, kill_y = bad_y;
11248 static int xy[4][2] =
11256 for (i = 0; i < NUM_DIRECTIONS; i++)
11260 x = bad_x + xy[i][0];
11261 y = bad_y + xy[i][1];
11262 if (!IN_LEV_FIELD(x, y))
11265 element = Feld[x][y];
11266 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11267 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11275 if (kill_x != bad_x || kill_y != bad_y)
11276 Bang(bad_x, bad_y);
11279 void KillHero(struct PlayerInfo *player)
11281 int jx = player->jx, jy = player->jy;
11283 if (!player->active)
11286 /* remove accessible field at the player's position */
11287 Feld[jx][jy] = EL_EMPTY;
11289 /* deactivate shield (else Bang()/Explode() would not work right) */
11290 player->shield_normal_time_left = 0;
11291 player->shield_deadly_time_left = 0;
11297 static void KillHeroUnlessEnemyProtected(int x, int y)
11299 if (!PLAYER_ENEMY_PROTECTED(x, y))
11300 KillHero(PLAYERINFO(x, y));
11303 static void KillHeroUnlessExplosionProtected(int x, int y)
11305 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11306 KillHero(PLAYERINFO(x, y));
11309 void BuryHero(struct PlayerInfo *player)
11311 int jx = player->jx, jy = player->jy;
11313 if (!player->active)
11317 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11319 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11321 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11323 player->GameOver = TRUE;
11324 RemoveHero(player);
11327 void RemoveHero(struct PlayerInfo *player)
11329 int jx = player->jx, jy = player->jy;
11330 int i, found = FALSE;
11332 player->present = FALSE;
11333 player->active = FALSE;
11335 if (!ExplodeField[jx][jy])
11336 StorePlayer[jx][jy] = 0;
11338 for (i = 0; i < MAX_PLAYERS; i++)
11339 if (stored_player[i].active)
11343 AllPlayersGone = TRUE;
11350 =============================================================================
11351 checkDiagonalPushing()
11352 -----------------------------------------------------------------------------
11353 check if diagonal input device direction results in pushing of object
11354 (by checking if the alternative direction is walkable, diggable, ...)
11355 =============================================================================
11358 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11359 int x, int y, int real_dx, int real_dy)
11361 int jx, jy, dx, dy, xx, yy;
11363 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11366 /* diagonal direction: check alternative direction */
11371 xx = jx + (dx == 0 ? real_dx : 0);
11372 yy = jy + (dy == 0 ? real_dy : 0);
11374 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11378 =============================================================================
11380 -----------------------------------------------------------------------------
11381 x, y: field next to player (non-diagonal) to try to dig to
11382 real_dx, real_dy: direction as read from input device (can be diagonal)
11383 =============================================================================
11386 int DigField(struct PlayerInfo *player,
11387 int oldx, int oldy, int x, int y,
11388 int real_dx, int real_dy, int mode)
11391 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11393 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11394 boolean player_was_pushing = player->is_pushing;
11395 int jx = oldx, jy = oldy;
11396 int dx = x - jx, dy = y - jy;
11397 int nextx = x + dx, nexty = y + dy;
11398 int move_direction = (dx == -1 ? MV_LEFT :
11399 dx == +1 ? MV_RIGHT :
11401 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11402 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11404 int dig_side = MV_DIR_OPPOSITE(move_direction);
11406 static int trigger_sides[4] =
11408 CH_SIDE_RIGHT, /* moving left */
11409 CH_SIDE_LEFT, /* moving right */
11410 CH_SIDE_BOTTOM, /* moving up */
11411 CH_SIDE_TOP, /* moving down */
11413 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11415 int old_element = Feld[jx][jy];
11418 if (is_player) /* function can also be called by EL_PENGUIN */
11420 if (player->MovPos == 0)
11422 player->is_digging = FALSE;
11423 player->is_collecting = FALSE;
11426 if (player->MovPos == 0) /* last pushing move finished */
11427 player->is_pushing = FALSE;
11429 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11431 player->is_switching = FALSE;
11432 #if USE_NEW_PUSH_DELAY
11433 player->push_delay = -1;
11435 player->push_delay = 0;
11438 return MF_NO_ACTION;
11442 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11443 return MF_NO_ACTION;
11448 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11450 if (IS_TUBE(Feld[jx][jy]) ||
11451 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11455 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11456 int tube_leave_directions[][2] =
11458 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11459 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11460 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11461 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11462 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11463 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11464 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11465 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11466 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11467 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11468 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11469 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11472 while (tube_leave_directions[i][0] != tube_element)
11475 if (tube_leave_directions[i][0] == -1) /* should not happen */
11479 if (!(tube_leave_directions[i][1] & move_direction))
11480 return MF_NO_ACTION; /* tube has no opening in this direction */
11485 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11486 old_element = Back[jx][jy];
11487 #if USE_BACK_WALKABLE_BUGFIX
11489 /* in case of element dropped at player position, check background */
11490 else if (Back[jx][jy] != EL_EMPTY &&
11491 game.engine_version >= VERSION_IDENT(2,2,0,0))
11492 old_element = Back[jx][jy];
11497 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11498 return MF_NO_ACTION; /* field has no opening in this direction */
11500 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11501 return MF_NO_ACTION; /* field has no opening in this direction */
11503 element = Feld[x][y];
11505 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11506 return MF_NO_ACTION;
11508 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11509 game.engine_version >= VERSION_IDENT(2,2,0,0))
11510 return MF_NO_ACTION;
11513 if (game.gravity && is_player && !player->is_auto_moving &&
11514 canFallDown(player) && move_direction != MV_DOWN &&
11515 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11516 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11520 if (element == EL_EMPTY_SPACE &&
11521 game.gravity && !player->is_auto_moving &&
11522 canFallDown(player) && move_direction != MV_DOWN)
11523 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11529 case EL_SP_PORT_LEFT:
11530 case EL_SP_PORT_RIGHT:
11531 case EL_SP_PORT_UP:
11532 case EL_SP_PORT_DOWN:
11533 case EL_SP_PORT_HORIZONTAL:
11534 case EL_SP_PORT_VERTICAL:
11535 case EL_SP_PORT_ANY:
11536 case EL_SP_GRAVITY_PORT_LEFT:
11537 case EL_SP_GRAVITY_PORT_RIGHT:
11538 case EL_SP_GRAVITY_PORT_UP:
11539 case EL_SP_GRAVITY_PORT_DOWN:
11541 if (!canEnterSupaplexPort(x, y, dx, dy))
11542 return MF_NO_ACTION;
11545 element != EL_SP_PORT_LEFT &&
11546 element != EL_SP_GRAVITY_PORT_LEFT &&
11547 element != EL_SP_PORT_HORIZONTAL &&
11548 element != EL_SP_PORT_ANY) ||
11550 element != EL_SP_PORT_RIGHT &&
11551 element != EL_SP_GRAVITY_PORT_RIGHT &&
11552 element != EL_SP_PORT_HORIZONTAL &&
11553 element != EL_SP_PORT_ANY) ||
11555 element != EL_SP_PORT_UP &&
11556 element != EL_SP_GRAVITY_PORT_UP &&
11557 element != EL_SP_PORT_VERTICAL &&
11558 element != EL_SP_PORT_ANY) ||
11560 element != EL_SP_PORT_DOWN &&
11561 element != EL_SP_GRAVITY_PORT_DOWN &&
11562 element != EL_SP_PORT_VERTICAL &&
11563 element != EL_SP_PORT_ANY) ||
11564 !IN_LEV_FIELD(nextx, nexty) ||
11565 !IS_FREE(nextx, nexty))
11566 return MF_NO_ACTION;
11569 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11570 element == EL_SP_GRAVITY_PORT_RIGHT ||
11571 element == EL_SP_GRAVITY_PORT_UP ||
11572 element == EL_SP_GRAVITY_PORT_DOWN)
11573 game.gravity = !game.gravity;
11575 /* automatically move to the next field with double speed */
11576 player->programmed_action = move_direction;
11578 if (player->move_delay_reset_counter == 0)
11580 player->move_delay_reset_counter = 2; /* two double speed steps */
11582 DOUBLE_PLAYER_SPEED(player);
11585 player->move_delay_reset_counter = 2;
11587 DOUBLE_PLAYER_SPEED(player);
11591 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11594 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11600 case EL_TUBE_VERTICAL:
11601 case EL_TUBE_HORIZONTAL:
11602 case EL_TUBE_VERTICAL_LEFT:
11603 case EL_TUBE_VERTICAL_RIGHT:
11604 case EL_TUBE_HORIZONTAL_UP:
11605 case EL_TUBE_HORIZONTAL_DOWN:
11606 case EL_TUBE_LEFT_UP:
11607 case EL_TUBE_LEFT_DOWN:
11608 case EL_TUBE_RIGHT_UP:
11609 case EL_TUBE_RIGHT_DOWN:
11612 int tube_enter_directions[][2] =
11614 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11615 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11616 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11617 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11618 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11619 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11620 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11621 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11622 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11623 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11624 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11625 { -1, MV_NO_MOVING }
11628 while (tube_enter_directions[i][0] != element)
11631 if (tube_enter_directions[i][0] == -1) /* should not happen */
11635 if (!(tube_enter_directions[i][1] & move_direction))
11636 return MF_NO_ACTION; /* tube has no opening in this direction */
11638 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11646 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11648 if (IS_WALKABLE(element))
11651 int sound_element = SND_ELEMENT(element);
11652 int sound_action = ACTION_WALKING;
11655 if (!ACCESS_FROM(element, opposite_direction))
11656 return MF_NO_ACTION; /* field not accessible from this direction */
11660 if (element == EL_EMPTY_SPACE &&
11661 game.gravity && !player->is_auto_moving &&
11662 canFallDown(player) && move_direction != MV_DOWN)
11663 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11666 if (IS_RND_GATE(element))
11668 if (!player->key[RND_GATE_NR(element)])
11669 return MF_NO_ACTION;
11671 else if (IS_RND_GATE_GRAY(element))
11673 if (!player->key[RND_GATE_GRAY_NR(element)])
11674 return MF_NO_ACTION;
11676 else if (element == EL_EXIT_OPEN ||
11677 element == EL_SP_EXIT_OPEN ||
11678 element == EL_SP_EXIT_OPENING)
11680 sound_action = ACTION_PASSING; /* player is passing exit */
11682 else if (element == EL_EMPTY)
11684 sound_action = ACTION_MOVING; /* nothing to walk on */
11687 /* play sound from background or player, whatever is available */
11688 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11689 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11691 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11696 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11698 else if (IS_PASSABLE(element))
11702 if (!canPassField(x, y, move_direction))
11703 return MF_NO_ACTION;
11708 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11709 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11710 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11711 return MF_NO_ACTION;
11713 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11714 return MF_NO_ACTION;
11719 if (!ACCESS_FROM(element, opposite_direction))
11720 return MF_NO_ACTION; /* field not accessible from this direction */
11722 if (IS_CUSTOM_ELEMENT(element) &&
11723 !ACCESS_FROM(element, opposite_direction))
11724 return MF_NO_ACTION; /* field not accessible from this direction */
11728 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11729 return MF_NO_ACTION;
11734 if (IS_EM_GATE(element))
11736 if (!player->key[EM_GATE_NR(element)])
11737 return MF_NO_ACTION;
11739 else if (IS_EM_GATE_GRAY(element))
11741 if (!player->key[EM_GATE_GRAY_NR(element)])
11742 return MF_NO_ACTION;
11744 else if (IS_SP_PORT(element))
11746 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11747 element == EL_SP_GRAVITY_PORT_RIGHT ||
11748 element == EL_SP_GRAVITY_PORT_UP ||
11749 element == EL_SP_GRAVITY_PORT_DOWN)
11750 game.gravity = !game.gravity;
11751 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11752 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11753 element == EL_SP_GRAVITY_ON_PORT_UP ||
11754 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11755 game.gravity = TRUE;
11756 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11757 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11758 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11759 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11760 game.gravity = FALSE;
11763 /* automatically move to the next field with double speed */
11764 player->programmed_action = move_direction;
11766 if (player->move_delay_reset_counter == 0)
11768 player->move_delay_reset_counter = 2; /* two double speed steps */
11770 DOUBLE_PLAYER_SPEED(player);
11773 player->move_delay_reset_counter = 2;
11775 DOUBLE_PLAYER_SPEED(player);
11778 PlayLevelSoundAction(x, y, ACTION_PASSING);
11782 else if (IS_DIGGABLE(element))
11786 if (mode != DF_SNAP)
11789 GfxElement[x][y] = GFX_ELEMENT(element);
11792 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11794 player->is_digging = TRUE;
11797 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11799 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11800 player->index_bit, dig_side);
11803 if (mode == DF_SNAP)
11804 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11809 else if (IS_COLLECTIBLE(element))
11813 if (is_player && mode != DF_SNAP)
11815 GfxElement[x][y] = element;
11816 player->is_collecting = TRUE;
11819 if (element == EL_SPEED_PILL)
11820 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11821 else if (element == EL_EXTRA_TIME && level.time > 0)
11824 DrawGameValue_Time(TimeLeft);
11826 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11828 player->shield_normal_time_left += 10;
11829 if (element == EL_SHIELD_DEADLY)
11830 player->shield_deadly_time_left += 10;
11832 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11834 if (player->inventory_size < MAX_INVENTORY_SIZE)
11835 player->inventory_element[player->inventory_size++] = element;
11837 DrawGameValue_Dynamite(local_player->inventory_size);
11839 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11841 player->dynabomb_count++;
11842 player->dynabombs_left++;
11844 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11846 player->dynabomb_size++;
11848 else if (element == EL_DYNABOMB_INCREASE_POWER)
11850 player->dynabomb_xl = TRUE;
11852 else if (IS_KEY(element))
11854 player->key[KEY_NR(element)] = TRUE;
11856 DrawGameValue_Keys(player->key);
11858 redraw_mask |= REDRAW_DOOR_1;
11860 else if (IS_ENVELOPE(element))
11863 player->show_envelope = element;
11865 ShowEnvelope(element - EL_ENVELOPE_1);
11868 else if (IS_DROPPABLE(element) ||
11869 IS_THROWABLE(element)) /* can be collected and dropped */
11873 if (element_info[element].collect_count == 0)
11874 player->inventory_infinite_element = element;
11876 for (i = 0; i < element_info[element].collect_count; i++)
11877 if (player->inventory_size < MAX_INVENTORY_SIZE)
11878 player->inventory_element[player->inventory_size++] = element;
11880 DrawGameValue_Dynamite(local_player->inventory_size);
11882 else if (element_info[element].collect_count > 0)
11884 local_player->gems_still_needed -=
11885 element_info[element].collect_count;
11886 if (local_player->gems_still_needed < 0)
11887 local_player->gems_still_needed = 0;
11889 DrawGameValue_Emeralds(local_player->gems_still_needed);
11892 RaiseScoreElement(element);
11893 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11896 CheckTriggeredElementChangeByPlayer(x, y, element,
11897 CE_PLAYER_COLLECTS_X,
11898 player->index_bit, dig_side);
11901 if (mode == DF_SNAP)
11902 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11907 else if (IS_PUSHABLE(element))
11909 if (mode == DF_SNAP && element != EL_BD_ROCK)
11910 return MF_NO_ACTION;
11912 if (CAN_FALL(element) && dy)
11913 return MF_NO_ACTION;
11915 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11916 !(element == EL_SPRING && level.use_spring_bug))
11917 return MF_NO_ACTION;
11920 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11921 ((move_direction & MV_VERTICAL &&
11922 ((element_info[element].move_pattern & MV_LEFT &&
11923 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11924 (element_info[element].move_pattern & MV_RIGHT &&
11925 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11926 (move_direction & MV_HORIZONTAL &&
11927 ((element_info[element].move_pattern & MV_UP &&
11928 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11929 (element_info[element].move_pattern & MV_DOWN &&
11930 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11931 return MF_NO_ACTION;
11935 /* do not push elements already moving away faster than player */
11936 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11937 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11938 return MF_NO_ACTION;
11940 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11941 return MF_NO_ACTION;
11947 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11949 if (player->push_delay_value == -1 || !player_was_pushing)
11950 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11952 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11954 if (player->push_delay_value == -1)
11955 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11958 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11960 if (player->push_delay_value == -1 || !player_was_pushing)
11961 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11964 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11966 if (!player->is_pushing)
11967 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11971 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11972 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11973 !player_is_pushing))
11974 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11977 if (!player->is_pushing &&
11978 game.engine_version >= VERSION_IDENT(2,2,0,7))
11979 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11983 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11984 player->push_delay, player->push_delay_value,
11985 FrameCounter, game.engine_version,
11986 player_was_pushing, player->is_pushing,
11987 element, element_info[element].token_name,
11988 GET_NEW_PUSH_DELAY(element));
11991 player->is_pushing = TRUE;
11993 if (!(IN_LEV_FIELD(nextx, nexty) &&
11994 (IS_FREE(nextx, nexty) ||
11995 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11996 IS_SB_ELEMENT(element)))))
11997 return MF_NO_ACTION;
11999 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12000 return MF_NO_ACTION;
12002 #if USE_NEW_PUSH_DELAY
12005 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
12006 printf("::: ALERT: %d, %d [%d / %d]\n",
12007 player->push_delay, player->push_delay2,
12008 FrameCounter, FrameCounter / 50);
12011 if (player->push_delay == -1) /* new pushing; restart delay */
12012 player->push_delay = 0;
12014 if (player->push_delay == 0) /* new pushing; restart delay */
12015 player->push_delay = FrameCounter;
12018 #if USE_NEW_PUSH_DELAY
12020 if ( (player->push_delay > 0) != (!xxx_fr) )
12021 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
12022 player->push_delay,
12023 xxx_pdv2, player->push_delay2, player->push_delay_value,
12024 FrameCounter, FrameCounter / 50);
12028 if (player->push_delay > 0 &&
12029 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12030 element != EL_SPRING && element != EL_BALLOON)
12033 if (player->push_delay < player->push_delay_value &&
12034 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12035 element != EL_SPRING && element != EL_BALLOON)
12039 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12040 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12041 element != EL_SPRING && element != EL_BALLOON)
12044 /* make sure that there is no move delay before next try to push */
12045 #if USE_NEW_MOVE_DELAY
12046 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12047 player->move_delay = 0;
12049 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12050 player->move_delay = INITIAL_MOVE_DELAY_OFF;
12053 return MF_NO_ACTION;
12057 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12060 if (IS_SB_ELEMENT(element))
12062 if (element == EL_SOKOBAN_FIELD_FULL)
12064 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12065 local_player->sokobanfields_still_needed++;
12068 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12070 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12071 local_player->sokobanfields_still_needed--;
12074 Feld[x][y] = EL_SOKOBAN_OBJECT;
12076 if (Back[x][y] == Back[nextx][nexty])
12077 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12078 else if (Back[x][y] != 0)
12079 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12082 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12085 if (local_player->sokobanfields_still_needed == 0 &&
12086 game.emulation == EMU_SOKOBAN)
12088 player->LevelSolved = player->GameOver = TRUE;
12089 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12093 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12095 InitMovingField(x, y, move_direction);
12096 GfxAction[x][y] = ACTION_PUSHING;
12098 if (mode == DF_SNAP)
12099 ContinueMoving(x, y);
12101 MovPos[x][y] = (dx != 0 ? dx : dy);
12103 Pushed[x][y] = TRUE;
12104 Pushed[nextx][nexty] = TRUE;
12106 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12107 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12109 player->push_delay_value = -1; /* get new value later */
12111 #if USE_PUSH_BUGFIX
12112 /* now: check for element change _after_ element has been pushed! */
12114 if (game.use_change_when_pushing_bug)
12116 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12119 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12120 player->index_bit, dig_side);
12121 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12122 player->index_bit, dig_side);
12128 /* check for element change _after_ element has been pushed! */
12132 /* !!! TEST ONLY !!! */
12133 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12134 player->index_bit, dig_side);
12135 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12136 player->index_bit, dig_side);
12138 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12139 player->index_bit, dig_side);
12140 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12141 player->index_bit, dig_side);
12149 else if (IS_SWITCHABLE(element))
12151 if (PLAYER_SWITCHING(player, x, y))
12153 CheckTriggeredElementChangeByPlayer(x,y, element,
12154 CE_PLAYER_PRESSES_X,
12155 player->index_bit, dig_side);
12160 player->is_switching = TRUE;
12161 player->switch_x = x;
12162 player->switch_y = y;
12164 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12166 if (element == EL_ROBOT_WHEEL)
12168 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12172 DrawLevelField(x, y);
12174 else if (element == EL_SP_TERMINAL)
12178 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12180 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12182 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12183 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12186 else if (IS_BELT_SWITCH(element))
12188 ToggleBeltSwitch(x, y);
12190 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12191 element == EL_SWITCHGATE_SWITCH_DOWN)
12193 ToggleSwitchgateSwitch(x, y);
12195 else if (element == EL_LIGHT_SWITCH ||
12196 element == EL_LIGHT_SWITCH_ACTIVE)
12198 ToggleLightSwitch(x, y);
12201 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12202 SND_LIGHT_SWITCH_ACTIVATING :
12203 SND_LIGHT_SWITCH_DEACTIVATING);
12206 else if (element == EL_TIMEGATE_SWITCH)
12208 ActivateTimegateSwitch(x, y);
12210 else if (element == EL_BALLOON_SWITCH_LEFT ||
12211 element == EL_BALLOON_SWITCH_RIGHT ||
12212 element == EL_BALLOON_SWITCH_UP ||
12213 element == EL_BALLOON_SWITCH_DOWN ||
12214 element == EL_BALLOON_SWITCH_ANY)
12216 if (element == EL_BALLOON_SWITCH_ANY)
12217 game.balloon_dir = move_direction;
12219 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12220 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12221 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12222 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12225 else if (element == EL_LAMP)
12227 Feld[x][y] = EL_LAMP_ACTIVE;
12228 local_player->lights_still_needed--;
12230 ResetGfxAnimation(x, y);
12231 DrawLevelField(x, y);
12233 else if (element == EL_TIME_ORB_FULL)
12235 Feld[x][y] = EL_TIME_ORB_EMPTY;
12237 DrawGameValue_Time(TimeLeft);
12239 ResetGfxAnimation(x, y);
12240 DrawLevelField(x, y);
12243 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12247 CheckTriggeredElementChangeByPlayer(x, y, element,
12249 player->index_bit, dig_side);
12251 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12252 player->index_bit, dig_side);
12258 if (!PLAYER_SWITCHING(player, x, y))
12260 player->is_switching = TRUE;
12261 player->switch_x = x;
12262 player->switch_y = y;
12265 /* !!! TEST ONLY !!! */
12266 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12267 player->index_bit, dig_side);
12268 CheckTriggeredElementChangeByPlayer(x, y, element,
12270 player->index_bit, dig_side);
12272 CheckTriggeredElementChangeByPlayer(x, y, element,
12274 player->index_bit, dig_side);
12275 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12276 player->index_bit, dig_side);
12281 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12282 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12283 player->index_bit, dig_side);
12284 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12285 player->index_bit, dig_side);
12287 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12288 player->index_bit, dig_side);
12289 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12290 player->index_bit, dig_side);
12294 return MF_NO_ACTION;
12297 #if USE_NEW_PUSH_DELAY
12298 player->push_delay = -1;
12300 player->push_delay = 0;
12303 #if USE_PENGUIN_COLLECT_BUGFIX
12304 if (is_player) /* function can also be called by EL_PENGUIN */
12307 if (Feld[x][y] != element) /* really digged/collected something */
12308 player->is_collecting = !player->is_digging;
12314 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12316 int jx = player->jx, jy = player->jy;
12317 int x = jx + dx, y = jy + dy;
12318 int snap_direction = (dx == -1 ? MV_LEFT :
12319 dx == +1 ? MV_RIGHT :
12321 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12324 if (player->MovPos != 0)
12327 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12331 if (!player->active || !IN_LEV_FIELD(x, y))
12339 if (player->MovPos == 0)
12340 player->is_pushing = FALSE;
12342 player->is_snapping = FALSE;
12344 if (player->MovPos == 0)
12346 player->is_moving = FALSE;
12347 player->is_digging = FALSE;
12348 player->is_collecting = FALSE;
12354 if (player->is_snapping)
12357 player->MovDir = snap_direction;
12360 if (player->MovPos == 0)
12363 player->is_moving = FALSE;
12364 player->is_digging = FALSE;
12365 player->is_collecting = FALSE;
12368 player->is_dropping = FALSE;
12370 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12373 player->is_snapping = TRUE;
12376 if (player->MovPos == 0)
12379 player->is_moving = FALSE;
12380 player->is_digging = FALSE;
12381 player->is_collecting = FALSE;
12385 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12386 DrawLevelField(player->last_jx, player->last_jy);
12389 DrawLevelField(x, y);
12398 boolean DropElement(struct PlayerInfo *player)
12400 int old_element, new_element;
12401 int dropx = player->jx, dropy = player->jy;
12402 int drop_direction = player->MovDir;
12404 int drop_side = drop_direction;
12406 static int trigger_sides[4] =
12408 CH_SIDE_LEFT, /* dropping left */
12409 CH_SIDE_RIGHT, /* dropping right */
12410 CH_SIDE_TOP, /* dropping up */
12411 CH_SIDE_BOTTOM, /* dropping down */
12413 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12415 int drop_element = (player->inventory_size > 0 ?
12416 player->inventory_element[player->inventory_size - 1] :
12417 player->inventory_infinite_element != EL_UNDEFINED ?
12418 player->inventory_infinite_element :
12419 player->dynabombs_left > 0 ?
12420 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12423 #if USE_DROP_BUGFIX
12424 /* do not drop an element on top of another element; when holding drop key
12425 pressed without moving, dropped element must move away before the next
12426 element can be dropped (this is especially important if the next element
12427 is dynamite, which can be placed on background for historical reasons) */
12428 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12432 if (IS_THROWABLE(drop_element))
12434 dropx += GET_DX_FROM_DIR(drop_direction);
12435 dropy += GET_DY_FROM_DIR(drop_direction);
12437 if (!IN_LEV_FIELD(dropx, dropy))
12441 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12442 new_element = drop_element; /* default: no change when dropping */
12444 /* check if player is active, not moving and ready to drop */
12445 if (!player->active || player->MovPos || player->drop_delay > 0)
12448 /* check if player has anything that can be dropped */
12450 if (new_element == EL_UNDEFINED)
12453 if (player->inventory_size == 0 &&
12454 player->inventory_infinite_element == EL_UNDEFINED &&
12455 player->dynabombs_left == 0)
12459 /* check if anything can be dropped at the current position */
12460 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12463 /* collected custom elements can only be dropped on empty fields */
12465 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12468 if (player->inventory_size > 0 &&
12469 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12470 && old_element != EL_EMPTY)
12474 if (old_element != EL_EMPTY)
12475 Back[dropx][dropy] = old_element; /* store old element on this field */
12477 ResetGfxAnimation(dropx, dropy);
12478 ResetRandomAnimationValue(dropx, dropy);
12480 if (player->inventory_size > 0 ||
12481 player->inventory_infinite_element != EL_UNDEFINED)
12483 if (player->inventory_size > 0)
12485 player->inventory_size--;
12488 new_element = player->inventory_element[player->inventory_size];
12491 DrawGameValue_Dynamite(local_player->inventory_size);
12493 if (new_element == EL_DYNAMITE)
12494 new_element = EL_DYNAMITE_ACTIVE;
12495 else if (new_element == EL_SP_DISK_RED)
12496 new_element = EL_SP_DISK_RED_ACTIVE;
12499 Feld[dropx][dropy] = new_element;
12501 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12502 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12503 el2img(Feld[dropx][dropy]), 0);
12505 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12508 /* needed if previous element just changed to "empty" in the last frame */
12509 Changed[dropx][dropy] = FALSE; /* allow another change */
12513 /* !!! TEST ONLY !!! */
12514 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12515 player->index_bit, drop_side);
12516 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12518 player->index_bit, drop_side);
12520 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12522 player->index_bit, drop_side);
12523 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12524 player->index_bit, drop_side);
12527 TestIfElementTouchesCustomElement(dropx, dropy);
12529 else /* player is dropping a dyna bomb */
12531 player->dynabombs_left--;
12534 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12537 Feld[dropx][dropy] = new_element;
12539 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12540 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12541 el2img(Feld[dropx][dropy]), 0);
12543 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12550 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12553 InitField_WithBug1(dropx, dropy, FALSE);
12555 InitField(dropx, dropy, FALSE);
12556 if (CAN_MOVE(Feld[dropx][dropy]))
12557 InitMovDir(dropx, dropy);
12561 new_element = Feld[dropx][dropy]; /* element might have changed */
12563 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12564 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12567 int move_stepsize = element_info[new_element].move_stepsize;
12569 int move_direction, nextx, nexty;
12571 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12572 MovDir[dropx][dropy] = drop_direction;
12574 move_direction = MovDir[dropx][dropy];
12575 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12576 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12579 Changed[dropx][dropy] = FALSE; /* allow another change */
12580 CheckCollision[dropx][dropy] = 2;
12583 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12586 WasJustMoving[dropx][dropy] = 3;
12589 InitMovingField(dropx, dropy, move_direction);
12590 ContinueMoving(dropx, dropy);
12595 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12598 Changed[dropx][dropy] = FALSE; /* allow another change */
12601 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12603 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12604 CE_HITTING_SOMETHING, move_direction);
12612 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12617 player->drop_delay = 8 + 8 + 8;
12621 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12626 player->is_dropping = TRUE;
12628 #if USE_DROP_BUGFIX
12629 player->drop_x = dropx;
12630 player->drop_y = dropy;
12636 /* ------------------------------------------------------------------------- */
12637 /* game sound playing functions */
12638 /* ------------------------------------------------------------------------- */
12640 static int *loop_sound_frame = NULL;
12641 static int *loop_sound_volume = NULL;
12643 void InitPlayLevelSound()
12645 int num_sounds = getSoundListSize();
12647 checked_free(loop_sound_frame);
12648 checked_free(loop_sound_volume);
12650 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12651 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12654 static void PlayLevelSound(int x, int y, int nr)
12656 int sx = SCREENX(x), sy = SCREENY(y);
12657 int volume, stereo_position;
12658 int max_distance = 8;
12659 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12661 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12662 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12665 if (!IN_LEV_FIELD(x, y) ||
12666 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12667 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12670 volume = SOUND_MAX_VOLUME;
12672 if (!IN_SCR_FIELD(sx, sy))
12674 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12675 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12677 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12680 stereo_position = (SOUND_MAX_LEFT +
12681 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12682 (SCR_FIELDX + 2 * max_distance));
12684 if (IS_LOOP_SOUND(nr))
12686 /* This assures that quieter loop sounds do not overwrite louder ones,
12687 while restarting sound volume comparison with each new game frame. */
12689 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12692 loop_sound_volume[nr] = volume;
12693 loop_sound_frame[nr] = FrameCounter;
12696 PlaySoundExt(nr, volume, stereo_position, type);
12699 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12701 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12702 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12703 y < LEVELY(BY1) ? LEVELY(BY1) :
12704 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12708 static void PlayLevelSoundAction(int x, int y, int action)
12710 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12713 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12715 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12717 if (sound_effect != SND_UNDEFINED)
12718 PlayLevelSound(x, y, sound_effect);
12721 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12724 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12726 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12727 PlayLevelSound(x, y, sound_effect);
12730 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12732 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12734 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12735 PlayLevelSound(x, y, sound_effect);
12738 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12740 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12742 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12743 StopSound(sound_effect);
12746 static void PlayLevelMusic()
12748 if (levelset.music[level_nr] != MUS_UNDEFINED)
12749 PlayMusic(levelset.music[level_nr]); /* from config file */
12751 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12754 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12756 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12759 if (sample == SAMPLE_bug)
12760 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12766 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12770 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12774 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12778 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12782 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12786 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12790 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12793 case SAMPLE_android_clone:
12794 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12797 case SAMPLE_android_move:
12798 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12801 case SAMPLE_spring:
12802 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12806 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12810 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12813 case SAMPLE_eater_eat:
12814 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12818 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12821 case SAMPLE_collect:
12822 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12825 case SAMPLE_diamond:
12826 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12829 case SAMPLE_squash:
12830 /* !!! CHECK THIS !!! */
12832 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12834 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12838 case SAMPLE_wonderfall:
12839 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12843 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12847 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12851 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12855 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12859 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12863 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12866 case SAMPLE_wonder:
12867 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12871 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12874 case SAMPLE_exit_open:
12875 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12878 case SAMPLE_exit_leave:
12879 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12882 case SAMPLE_dynamite:
12883 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12887 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12891 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12895 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12899 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12903 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12907 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12911 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12916 void RaiseScore(int value)
12918 local_player->score += value;
12920 DrawGameValue_Score(local_player->score);
12923 void RaiseScoreElement(int element)
12928 case EL_BD_DIAMOND:
12929 case EL_EMERALD_YELLOW:
12930 case EL_EMERALD_RED:
12931 case EL_EMERALD_PURPLE:
12932 case EL_SP_INFOTRON:
12933 RaiseScore(level.score[SC_EMERALD]);
12936 RaiseScore(level.score[SC_DIAMOND]);
12939 RaiseScore(level.score[SC_CRYSTAL]);
12942 RaiseScore(level.score[SC_PEARL]);
12945 case EL_BD_BUTTERFLY:
12946 case EL_SP_ELECTRON:
12947 RaiseScore(level.score[SC_BUG]);
12950 case EL_BD_FIREFLY:
12951 case EL_SP_SNIKSNAK:
12952 RaiseScore(level.score[SC_SPACESHIP]);
12955 case EL_DARK_YAMYAM:
12956 RaiseScore(level.score[SC_YAMYAM]);
12959 RaiseScore(level.score[SC_ROBOT]);
12962 RaiseScore(level.score[SC_PACMAN]);
12965 RaiseScore(level.score[SC_NUT]);
12968 case EL_SP_DISK_RED:
12969 case EL_DYNABOMB_INCREASE_NUMBER:
12970 case EL_DYNABOMB_INCREASE_SIZE:
12971 case EL_DYNABOMB_INCREASE_POWER:
12972 RaiseScore(level.score[SC_DYNAMITE]);
12974 case EL_SHIELD_NORMAL:
12975 case EL_SHIELD_DEADLY:
12976 RaiseScore(level.score[SC_SHIELD]);
12978 case EL_EXTRA_TIME:
12979 RaiseScore(level.score[SC_TIME_BONUS]);
12993 RaiseScore(level.score[SC_KEY]);
12996 RaiseScore(element_info[element].collect_score);
13001 void RequestQuitGame(boolean ask_if_really_quit)
13003 if (AllPlayersGone ||
13004 !ask_if_really_quit ||
13005 level_editor_test_game ||
13006 Request("Do you really want to quit the game ?",
13007 REQ_ASK | REQ_STAY_CLOSED))
13009 #if defined(NETWORK_AVALIABLE)
13010 if (options.network)
13011 SendToServer_StopPlaying();
13015 game_status = GAME_MODE_MAIN;
13023 if (tape.playing && tape.deactivate_display)
13024 TapeDeactivateDisplayOff(TRUE);
13027 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13030 if (tape.playing && tape.deactivate_display)
13031 TapeDeactivateDisplayOn();
13038 /* ---------- new game button stuff ---------------------------------------- */
13040 /* graphic position values for game buttons */
13041 #define GAME_BUTTON_XSIZE 30
13042 #define GAME_BUTTON_YSIZE 30
13043 #define GAME_BUTTON_XPOS 5
13044 #define GAME_BUTTON_YPOS 215
13045 #define SOUND_BUTTON_XPOS 5
13046 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13048 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13049 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13050 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13051 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13052 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13053 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13060 } gamebutton_info[NUM_GAME_BUTTONS] =
13063 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13068 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13069 GAME_CTRL_ID_PAUSE,
13073 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13078 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13079 SOUND_CTRL_ID_MUSIC,
13080 "background music on/off"
13083 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13084 SOUND_CTRL_ID_LOOPS,
13085 "sound loops on/off"
13088 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13089 SOUND_CTRL_ID_SIMPLE,
13090 "normal sounds on/off"
13094 void CreateGameButtons()
13098 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13100 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13101 struct GadgetInfo *gi;
13104 unsigned long event_mask;
13105 int gd_xoffset, gd_yoffset;
13106 int gd_x1, gd_x2, gd_y1, gd_y2;
13109 gd_xoffset = gamebutton_info[i].x;
13110 gd_yoffset = gamebutton_info[i].y;
13111 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13112 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13114 if (id == GAME_CTRL_ID_STOP ||
13115 id == GAME_CTRL_ID_PAUSE ||
13116 id == GAME_CTRL_ID_PLAY)
13118 button_type = GD_TYPE_NORMAL_BUTTON;
13120 event_mask = GD_EVENT_RELEASED;
13121 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13122 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13126 button_type = GD_TYPE_CHECK_BUTTON;
13128 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13129 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13130 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13131 event_mask = GD_EVENT_PRESSED;
13132 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13133 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13136 gi = CreateGadget(GDI_CUSTOM_ID, id,
13137 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13138 GDI_X, DX + gd_xoffset,
13139 GDI_Y, DY + gd_yoffset,
13140 GDI_WIDTH, GAME_BUTTON_XSIZE,
13141 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13142 GDI_TYPE, button_type,
13143 GDI_STATE, GD_BUTTON_UNPRESSED,
13144 GDI_CHECKED, checked,
13145 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13146 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13147 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13148 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13149 GDI_EVENT_MASK, event_mask,
13150 GDI_CALLBACK_ACTION, HandleGameButtons,
13154 Error(ERR_EXIT, "cannot create gadget");
13156 game_gadget[id] = gi;
13160 void FreeGameButtons()
13164 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13165 FreeGadget(game_gadget[i]);
13168 static void MapGameButtons()
13172 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13173 MapGadget(game_gadget[i]);
13176 void UnmapGameButtons()
13180 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13181 UnmapGadget(game_gadget[i]);
13184 static void HandleGameButtons(struct GadgetInfo *gi)
13186 int id = gi->custom_id;
13188 if (game_status != GAME_MODE_PLAYING)
13193 case GAME_CTRL_ID_STOP:
13194 RequestQuitGame(TRUE);
13197 case GAME_CTRL_ID_PAUSE:
13198 if (options.network)
13200 #if defined(NETWORK_AVALIABLE)
13202 SendToServer_ContinuePlaying();
13204 SendToServer_PausePlaying();
13208 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13211 case GAME_CTRL_ID_PLAY:
13214 #if defined(NETWORK_AVALIABLE)
13215 if (options.network)
13216 SendToServer_ContinuePlaying();
13220 tape.pausing = FALSE;
13221 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13226 case SOUND_CTRL_ID_MUSIC:
13227 if (setup.sound_music)
13229 setup.sound_music = FALSE;
13232 else if (audio.music_available)
13234 setup.sound = setup.sound_music = TRUE;
13236 SetAudioMode(setup.sound);
13242 case SOUND_CTRL_ID_LOOPS:
13243 if (setup.sound_loops)
13244 setup.sound_loops = FALSE;
13245 else if (audio.loops_available)
13247 setup.sound = setup.sound_loops = TRUE;
13248 SetAudioMode(setup.sound);
13252 case SOUND_CTRL_ID_SIMPLE:
13253 if (setup.sound_simple)
13254 setup.sound_simple = FALSE;
13255 else if (audio.sound_available)
13257 setup.sound = setup.sound_simple = TRUE;
13258 SetAudioMode(setup.sound);