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))
146 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
147 RND((c)->delay_random * (c)->delay_frames))
149 #define GET_TARGET_ELEMENT(e, ch) \
150 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
151 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
153 #define GET_VALID_PLAYER_ELEMENT(e) \
154 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
156 #define CAN_GROW_INTO(e) \
157 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
159 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
160 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
164 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
165 (CAN_MOVE_INTO_ACID(e) && \
166 Feld[x][y] == EL_ACID) || \
169 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
170 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
171 (CAN_MOVE_INTO_ACID(e) && \
172 Feld[x][y] == EL_ACID) || \
175 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
176 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
178 (CAN_MOVE_INTO_ACID(e) && \
179 Feld[x][y] == EL_ACID) || \
180 (DONT_COLLIDE_WITH(e) && \
182 !PLAYER_ENEMY_PROTECTED(x, y))))
185 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
186 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
188 (DONT_COLLIDE_WITH(e) && \
190 !PLAYER_ENEMY_PROTECTED(x, y))))
193 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
197 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
198 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
200 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
205 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
208 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
209 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
213 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
214 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
216 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
219 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
220 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
222 #define PIG_CAN_ENTER_FIELD(e, x, y) \
223 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
225 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
226 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
227 IS_FOOD_PENGUIN(Feld[x][y])))
228 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
229 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
231 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
234 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
239 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID) || \
243 Feld[x][y] == EL_DIAMOND))
245 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
246 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
247 (CAN_MOVE_INTO_ACID(e) && \
248 Feld[x][y] == EL_ACID) || \
249 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
251 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
252 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
253 (CAN_MOVE_INTO_ACID(e) && \
254 Feld[x][y] == EL_ACID) || \
255 IS_AMOEBOID(Feld[x][y])))
257 #define PIG_CAN_ENTER_FIELD(e, x, y) \
258 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
259 (CAN_MOVE_INTO_ACID(e) && \
260 Feld[x][y] == EL_ACID) || \
261 IS_FOOD_PIG(Feld[x][y])))
263 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
264 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
265 (CAN_MOVE_INTO_ACID(e) && \
266 Feld[x][y] == EL_ACID) || \
267 IS_FOOD_PENGUIN(Feld[x][y]) || \
268 Feld[x][y] == EL_EXIT_OPEN))
270 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
271 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
272 (CAN_MOVE_INTO_ACID(e) && \
273 Feld[x][y] == EL_ACID)))
275 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
276 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
277 (CAN_MOVE_INTO_ACID(e) && \
278 Feld[x][y] == EL_ACID) || \
281 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
282 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
283 (CAN_MOVE_INTO_ACID(e) && \
284 Feld[x][y] == EL_ACID)))
288 #define GROUP_NR(e) ((e) - EL_GROUP_START)
289 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
290 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
291 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
293 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
294 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
297 #define CE_ENTER_FIELD_COND(e, x, y) \
298 (!IS_PLAYER(x, y) && \
299 (Feld[x][y] == EL_ACID || \
300 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
302 #define CE_ENTER_FIELD_COND(e, x, y) \
303 (!IS_PLAYER(x, y) && \
304 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
307 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
308 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
310 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
311 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
313 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
314 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
315 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
316 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
318 /* game button identifiers */
319 #define GAME_CTRL_ID_STOP 0
320 #define GAME_CTRL_ID_PAUSE 1
321 #define GAME_CTRL_ID_PLAY 2
322 #define SOUND_CTRL_ID_MUSIC 3
323 #define SOUND_CTRL_ID_LOOPS 4
324 #define SOUND_CTRL_ID_SIMPLE 5
326 #define NUM_GAME_BUTTONS 6
329 /* forward declaration for internal use */
331 static void AdvanceFrameAndPlayerCounters(int);
333 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
334 static boolean MovePlayer(struct PlayerInfo *, int, int);
335 static void ScrollPlayer(struct PlayerInfo *, int);
336 static void ScrollScreen(struct PlayerInfo *, int);
338 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
340 static void InitBeltMovement(void);
341 static void CloseAllOpenTimegates(void);
342 static void CheckGravityMovement(struct PlayerInfo *);
343 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
344 static void KillHeroUnlessEnemyProtected(int, int);
345 static void KillHeroUnlessExplosionProtected(int, int);
347 static void TestIfPlayerTouchesCustomElement(int, int);
348 static void TestIfElementTouchesCustomElement(int, int);
349 static void TestIfElementHitsCustomElement(int, int, int);
351 static void TestIfElementSmashesCustomElement(int, int, int);
354 static void ChangeElement(int, int, int);
356 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
357 #define CheckTriggeredElementChange(x, y, e, ev) \
358 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
360 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
361 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
362 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
363 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
364 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
365 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
368 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
369 #define CheckElementChange(x, y, e, te, ev) \
370 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
371 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
372 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
373 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
374 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
375 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
376 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
378 static void PlayLevelSound(int, int, int);
379 static void PlayLevelSoundNearest(int, int, int);
380 static void PlayLevelSoundAction(int, int, int);
381 static void PlayLevelSoundElementAction(int, int, int, int);
382 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
383 static void PlayLevelSoundActionIfLoop(int, int, int);
384 static void StopLevelSoundActionIfLoop(int, int, int);
385 static void PlayLevelMusic();
387 static void MapGameButtons();
388 static void HandleGameButtons(struct GadgetInfo *);
390 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
393 /* ------------------------------------------------------------------------- */
394 /* definition of elements that automatically change to other elements after */
395 /* a specified time, eventually calling a function when changing */
396 /* ------------------------------------------------------------------------- */
398 /* forward declaration for changer functions */
399 static void InitBuggyBase(int x, int y);
400 static void WarnBuggyBase(int x, int y);
402 static void InitTrap(int x, int y);
403 static void ActivateTrap(int x, int y);
404 static void ChangeActiveTrap(int x, int y);
406 static void InitRobotWheel(int x, int y);
407 static void RunRobotWheel(int x, int y);
408 static void StopRobotWheel(int x, int y);
410 static void InitTimegateWheel(int x, int y);
411 static void RunTimegateWheel(int x, int y);
413 struct ChangingElementInfo
418 void (*pre_change_function)(int x, int y);
419 void (*change_function)(int x, int y);
420 void (*post_change_function)(int x, int y);
423 static struct ChangingElementInfo change_delay_list[] =
474 EL_SWITCHGATE_OPENING,
482 EL_SWITCHGATE_CLOSING,
483 EL_SWITCHGATE_CLOSED,
515 EL_ACID_SPLASH_RIGHT,
524 EL_SP_BUGGY_BASE_ACTIVATING,
531 EL_SP_BUGGY_BASE_ACTIVATING,
532 EL_SP_BUGGY_BASE_ACTIVE,
539 EL_SP_BUGGY_BASE_ACTIVE,
563 EL_ROBOT_WHEEL_ACTIVE,
571 EL_TIMEGATE_SWITCH_ACTIVE,
592 int push_delay_fixed, push_delay_random;
597 { EL_BALLOON, 0, 0 },
599 { EL_SOKOBAN_OBJECT, 2, 0 },
600 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
601 { EL_SATELLITE, 2, 0 },
602 { EL_SP_DISK_YELLOW, 2, 0 },
604 { EL_UNDEFINED, 0, 0 },
612 move_stepsize_list[] =
614 { EL_AMOEBA_DROP, 2 },
615 { EL_AMOEBA_DROPPING, 2 },
616 { EL_QUICKSAND_FILLING, 1 },
617 { EL_QUICKSAND_EMPTYING, 1 },
618 { EL_MAGIC_WALL_FILLING, 2 },
619 { EL_BD_MAGIC_WALL_FILLING, 2 },
620 { EL_MAGIC_WALL_EMPTYING, 2 },
621 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
631 collect_count_list[] =
634 { EL_BD_DIAMOND, 1 },
635 { EL_EMERALD_YELLOW, 1 },
636 { EL_EMERALD_RED, 1 },
637 { EL_EMERALD_PURPLE, 1 },
639 { EL_SP_INFOTRON, 1 },
651 access_direction_list[] =
653 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
654 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
655 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
656 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
657 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
658 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
659 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
660 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
661 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
662 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
663 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
665 { EL_SP_PORT_LEFT, MV_RIGHT },
666 { EL_SP_PORT_RIGHT, MV_LEFT },
667 { EL_SP_PORT_UP, MV_DOWN },
668 { EL_SP_PORT_DOWN, MV_UP },
669 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
670 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
671 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
672 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
673 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
674 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
675 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
676 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
677 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
678 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
679 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
680 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
681 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
682 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
683 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
685 { EL_UNDEFINED, MV_NO_MOVING }
688 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
690 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
691 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
692 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
693 IS_JUST_CHANGING(x, y))
695 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
698 void GetPlayerConfig()
700 if (!audio.sound_available)
701 setup.sound_simple = FALSE;
703 if (!audio.loops_available)
704 setup.sound_loops = FALSE;
706 if (!audio.music_available)
707 setup.sound_music = FALSE;
709 if (!video.fullscreen_available)
710 setup.fullscreen = FALSE;
712 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
714 SetAudioMode(setup.sound);
718 static int getBeltNrFromBeltElement(int element)
720 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
721 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
722 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
725 static int getBeltNrFromBeltActiveElement(int element)
727 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
728 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
729 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
732 static int getBeltNrFromBeltSwitchElement(int element)
734 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
735 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
736 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
739 static int getBeltDirNrFromBeltSwitchElement(int element)
741 static int belt_base_element[4] =
743 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
744 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
745 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
746 EL_CONVEYOR_BELT_4_SWITCH_LEFT
749 int belt_nr = getBeltNrFromBeltSwitchElement(element);
750 int belt_dir_nr = element - belt_base_element[belt_nr];
752 return (belt_dir_nr % 3);
755 static int getBeltDirFromBeltSwitchElement(int element)
757 static int belt_move_dir[3] =
764 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
766 return belt_move_dir[belt_dir_nr];
769 static void InitPlayerField(int x, int y, int element, boolean init_game)
771 if (element == EL_SP_MURPHY)
775 if (stored_player[0].present)
777 Feld[x][y] = EL_SP_MURPHY_CLONE;
783 stored_player[0].use_murphy_graphic = TRUE;
786 Feld[x][y] = EL_PLAYER_1;
792 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
793 int jx = player->jx, jy = player->jy;
795 player->present = TRUE;
797 player->block_last_field = (element == EL_SP_MURPHY ?
798 level.sp_block_last_field :
799 level.block_last_field);
801 #if USE_NEW_BLOCK_STYLE
804 /* ---------- initialize player's last field block delay --------------- */
806 /* always start with reliable default value (no adjustment needed) */
807 player->block_delay_adjustment = 0;
809 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
810 if (player->block_last_field && element == EL_SP_MURPHY)
811 player->block_delay_adjustment = 1;
813 /* special case 2: in game engines before 3.1.1, blocking was different */
814 if (game.use_block_last_field_bug)
815 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
818 /* blocking the last field when moving was corrected in version 3.1.1 */
819 if (game.use_block_last_field_bug)
821 /* even "not blocking" was blocking the last field for one frame */
822 level.block_delay = (level.block_last_field ? 7 : 1);
823 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
825 level.block_last_field = TRUE;
826 level.sp_block_last_field = TRUE;
830 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
831 level.block_delay = 8; /* when blocking, block 8 frames */
832 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
836 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
842 player->block_delay = (player->block_last_field ?
843 (element == EL_SP_MURPHY ?
844 level.sp_block_delay :
845 level.block_delay) : 0);
847 player->block_delay = (element == EL_SP_MURPHY ?
848 (player->block_last_field ? 7 : 1) :
849 (player->block_last_field ? 7 : 1));
855 printf("::: block_last_field == %d, block_delay = %d\n",
856 player->block_last_field, player->block_delay);
860 if (!options.network || player->connected)
862 player->active = TRUE;
864 /* remove potentially duplicate players */
865 if (StorePlayer[jx][jy] == Feld[x][y])
866 StorePlayer[jx][jy] = 0;
868 StorePlayer[x][y] = Feld[x][y];
872 printf("Player %d activated.\n", player->element_nr);
873 printf("[Local player is %d and currently %s.]\n",
874 local_player->element_nr,
875 local_player->active ? "active" : "not active");
879 Feld[x][y] = EL_EMPTY;
881 player->jx = player->last_jx = x;
882 player->jy = player->last_jy = y;
886 static void InitField(int x, int y, boolean init_game)
888 int element = Feld[x][y];
897 InitPlayerField(x, y, element, init_game);
900 case EL_SOKOBAN_FIELD_PLAYER:
901 element = Feld[x][y] = EL_PLAYER_1;
902 InitField(x, y, init_game);
904 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
905 InitField(x, y, init_game);
908 case EL_SOKOBAN_FIELD_EMPTY:
909 local_player->sokobanfields_still_needed++;
913 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
914 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
915 else if (x > 0 && Feld[x-1][y] == EL_ACID)
916 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
917 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
918 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
919 else if (y > 0 && Feld[x][y-1] == EL_ACID)
920 Feld[x][y] = EL_ACID_POOL_BOTTOM;
921 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
922 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
930 case EL_SPACESHIP_RIGHT:
931 case EL_SPACESHIP_UP:
932 case EL_SPACESHIP_LEFT:
933 case EL_SPACESHIP_DOWN:
935 case EL_BD_BUTTERFLY_RIGHT:
936 case EL_BD_BUTTERFLY_UP:
937 case EL_BD_BUTTERFLY_LEFT:
938 case EL_BD_BUTTERFLY_DOWN:
939 case EL_BD_BUTTERFLY:
940 case EL_BD_FIREFLY_RIGHT:
941 case EL_BD_FIREFLY_UP:
942 case EL_BD_FIREFLY_LEFT:
943 case EL_BD_FIREFLY_DOWN:
945 case EL_PACMAN_RIGHT:
969 if (y == lev_fieldy - 1)
971 Feld[x][y] = EL_AMOEBA_GROWING;
972 Store[x][y] = EL_AMOEBA_WET;
976 case EL_DYNAMITE_ACTIVE:
977 case EL_SP_DISK_RED_ACTIVE:
978 case EL_DYNABOMB_PLAYER_1_ACTIVE:
979 case EL_DYNABOMB_PLAYER_2_ACTIVE:
980 case EL_DYNABOMB_PLAYER_3_ACTIVE:
981 case EL_DYNABOMB_PLAYER_4_ACTIVE:
986 local_player->lights_still_needed++;
990 local_player->friends_still_needed++;
995 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1000 Feld[x][y] = EL_EMPTY;
1005 case EL_EM_KEY_1_FILE:
1006 Feld[x][y] = EL_EM_KEY_1;
1008 case EL_EM_KEY_2_FILE:
1009 Feld[x][y] = EL_EM_KEY_2;
1011 case EL_EM_KEY_3_FILE:
1012 Feld[x][y] = EL_EM_KEY_3;
1014 case EL_EM_KEY_4_FILE:
1015 Feld[x][y] = EL_EM_KEY_4;
1019 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1020 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1021 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1022 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1023 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1024 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1025 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1026 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1027 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1028 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1029 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1030 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1033 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1034 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1035 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1037 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1039 game.belt_dir[belt_nr] = belt_dir;
1040 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1042 else /* more than one switch -- set it like the first switch */
1044 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1049 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1051 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1054 case EL_LIGHT_SWITCH_ACTIVE:
1056 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1060 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1062 else if (IS_GROUP_ELEMENT(element))
1064 struct ElementGroupInfo *group = element_info[element].group;
1065 int last_anim_random_frame = gfx.anim_random_frame;
1068 if (group->choice_mode == ANIM_RANDOM)
1069 gfx.anim_random_frame = RND(group->num_elements_resolved);
1071 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1072 group->choice_mode, 0,
1075 if (group->choice_mode == ANIM_RANDOM)
1076 gfx.anim_random_frame = last_anim_random_frame;
1078 group->choice_pos++;
1080 Feld[x][y] = group->element_resolved[element_pos];
1082 InitField(x, y, init_game);
1088 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1090 InitField(x, y, init_game);
1092 /* not needed to call InitMovDir() -- already done by InitField()! */
1093 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1094 CAN_MOVE(Feld[x][y]))
1098 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1100 int old_element = Feld[x][y];
1102 InitField(x, y, init_game);
1104 /* not needed to call InitMovDir() -- already done by InitField()! */
1105 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1106 CAN_MOVE(old_element) &&
1107 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1110 /* this case is in fact a combination of not less than three bugs:
1111 first, it calls InitMovDir() for elements that can move, although this is
1112 already done by InitField(); then, it checks the element that was at this
1113 field _before_ the call to InitField() (which can change it); lastly, it
1114 was not called for "mole with direction" elements, which were treated as
1115 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1119 inline void DrawGameValue_Emeralds(int value)
1121 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1124 inline void DrawGameValue_Dynamite(int value)
1126 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1129 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1133 /* currently only 4 of 8 possible keys are displayed */
1134 for (i = 0; i < STD_NUM_KEYS; i++)
1137 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1138 el2edimg(EL_KEY_1 + i));
1140 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1141 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1142 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1146 inline void DrawGameValue_Score(int value)
1148 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1151 inline void DrawGameValue_Time(int value)
1154 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1156 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1159 inline void DrawGameValue_Level(int value)
1162 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1165 /* misuse area for displaying emeralds to draw bigger level number */
1166 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1167 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1169 /* now copy it to the area for displaying level number */
1170 BlitBitmap(drawto, drawto,
1171 DX_EMERALDS, DY_EMERALDS + 1,
1172 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1173 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1174 DX_LEVEL - 1, DY_LEVEL + 1);
1176 /* restore the area for displaying emeralds */
1177 DrawGameValue_Emeralds(local_player->gems_still_needed);
1179 /* yes, this is all really ugly :-) */
1183 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1186 int key[MAX_NUM_KEYS];
1189 for (i = 0; i < MAX_NUM_KEYS; i++)
1190 key[i] = key_bits & (1 << i);
1192 DrawGameValue_Level(level_nr);
1194 DrawGameValue_Emeralds(emeralds);
1195 DrawGameValue_Dynamite(dynamite);
1196 DrawGameValue_Score(score);
1197 DrawGameValue_Time(time);
1199 DrawGameValue_Keys(key);
1202 void DrawGameDoorValues()
1206 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1208 DrawGameDoorValues_EM();
1213 DrawGameValue_Level(level_nr);
1215 DrawGameValue_Emeralds(local_player->gems_still_needed);
1216 DrawGameValue_Dynamite(local_player->inventory_size);
1217 DrawGameValue_Score(local_player->score);
1218 DrawGameValue_Time(TimeLeft);
1220 for (i = 0; i < MAX_PLAYERS; i++)
1221 DrawGameValue_Keys(stored_player[i].key);
1224 static void resolve_group_element(int group_element, int recursion_depth)
1226 static int group_nr;
1227 static struct ElementGroupInfo *group;
1228 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1231 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1233 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1234 group_element - EL_GROUP_START + 1);
1236 /* replace element which caused too deep recursion by question mark */
1237 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1242 if (recursion_depth == 0) /* initialization */
1244 group = element_info[group_element].group;
1245 group_nr = group_element - EL_GROUP_START;
1247 group->num_elements_resolved = 0;
1248 group->choice_pos = 0;
1251 for (i = 0; i < actual_group->num_elements; i++)
1253 int element = actual_group->element[i];
1255 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1258 if (IS_GROUP_ELEMENT(element))
1259 resolve_group_element(element, recursion_depth + 1);
1262 group->element_resolved[group->num_elements_resolved++] = element;
1263 element_info[element].in_group[group_nr] = TRUE;
1268 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1270 printf("::: group %d: %d resolved elements\n",
1271 group_element - EL_GROUP_START, group->num_elements_resolved);
1272 for (i = 0; i < group->num_elements_resolved; i++)
1273 printf("::: - %d ['%s']\n", group->element_resolved[i],
1274 element_info[group->element_resolved[i]].token_name);
1281 =============================================================================
1283 -----------------------------------------------------------------------------
1284 initialize game engine due to level / tape version number
1285 =============================================================================
1288 static void InitGameEngine()
1292 /* set game engine from tape file when re-playing, else from level file */
1293 game.engine_version = (tape.playing ? tape.engine_version :
1294 level.game_version);
1296 /* ---------------------------------------------------------------------- */
1297 /* set flags for bugs and changes according to active game engine version */
1298 /* ---------------------------------------------------------------------- */
1301 Summary of bugfix/change:
1302 Fixed handling for custom elements that change when pushed by the player.
1304 Fixed/changed in version:
1308 Before 3.1.0, custom elements that "change when pushing" changed directly
1309 after the player started pushing them (until then handled in "DigField()").
1310 Since 3.1.0, these custom elements are not changed until the "pushing"
1311 move of the element is finished (now handled in "ContinueMoving()").
1313 Affected levels/tapes:
1314 The first condition is generally needed for all levels/tapes before version
1315 3.1.0, which might use the old behaviour before it was changed; known tapes
1316 that are affected are some tapes from the level set "Walpurgis Gardens" by
1318 The second condition is an exception from the above case and is needed for
1319 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1320 above (including some development versions of 3.1.0), but before it was
1321 known that this change would break tapes like the above and was fixed in
1322 3.1.1, so that the changed behaviour was active although the engine version
1323 while recording maybe was before 3.1.0. There is at least one tape that is
1324 affected by this exception, which is the tape for the one-level set "Bug
1325 Machine" by Juergen Bonhagen.
1328 game.use_change_when_pushing_bug =
1329 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1331 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1332 tape.game_version < VERSION_IDENT(3,1,1,0)));
1335 Summary of bugfix/change:
1336 Fixed handling for blocking the field the player leaves when moving.
1338 Fixed/changed in version:
1342 Before 3.1.1, when "block last field when moving" was enabled, the field
1343 the player is leaving when moving was blocked for the time of the move,
1344 and was directly unblocked afterwards. This resulted in the last field
1345 being blocked for exactly one less than the number of frames of one player
1346 move. Additionally, even when blocking was disabled, the last field was
1347 blocked for exactly one frame.
1348 Since 3.1.1, due to changes in player movement handling, the last field
1349 is not blocked at all when blocking is disabled. When blocking is enabled,
1350 the last field is blocked for exactly the number of frames of one player
1351 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1352 last field is blocked for exactly one more than the number of frames of
1355 Affected levels/tapes:
1356 (!!! yet to be determined -- probably many !!!)
1359 game.use_block_last_field_bug =
1360 (game.engine_version < VERSION_IDENT(3,1,1,0));
1362 /* ---------------------------------------------------------------------- */
1364 /* dynamically adjust element properties according to game engine version */
1365 InitElementPropertiesEngine(game.engine_version);
1368 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1369 printf(" tape version == %06d [%s] [file: %06d]\n",
1370 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1372 printf(" => game.engine_version == %06d\n", game.engine_version);
1375 /* ---------- recursively resolve group elements ------------------------- */
1377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1378 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1379 element_info[i].in_group[j] = FALSE;
1381 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1382 resolve_group_element(EL_GROUP_START + i, 0);
1384 /* ---------- initialize player's initial move delay --------------------- */
1386 #if USE_NEW_MOVE_DELAY
1387 /* dynamically adjust player properties according to level information */
1388 game.initial_move_delay_value =
1389 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1391 /* dynamically adjust player properties according to game engine version */
1392 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1393 game.initial_move_delay_value : 0);
1395 /* dynamically adjust player properties according to game engine version */
1396 game.initial_move_delay =
1397 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1398 INITIAL_MOVE_DELAY_OFF);
1400 /* dynamically adjust player properties according to level information */
1401 game.initial_move_delay_value =
1402 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1405 /* ---------- initialize player's initial push delay --------------------- */
1407 /* dynamically adjust player properties according to game engine version */
1408 game.initial_push_delay_value =
1409 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1411 /* ---------- initialize changing elements ------------------------------- */
1413 /* initialize changing elements information */
1414 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1416 struct ElementInfo *ei = &element_info[i];
1418 /* this pointer might have been changed in the level editor */
1419 ei->change = &ei->change_page[0];
1421 if (!IS_CUSTOM_ELEMENT(i))
1423 ei->change->target_element = EL_EMPTY_SPACE;
1424 ei->change->delay_fixed = 0;
1425 ei->change->delay_random = 0;
1426 ei->change->delay_frames = 1;
1429 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1431 ei->has_change_event[j] = FALSE;
1433 ei->event_page_nr[j] = 0;
1434 ei->event_page[j] = &ei->change_page[0];
1438 /* add changing elements from pre-defined list */
1439 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1441 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1442 struct ElementInfo *ei = &element_info[ch_delay->element];
1444 ei->change->target_element = ch_delay->target_element;
1445 ei->change->delay_fixed = ch_delay->change_delay;
1447 ei->change->pre_change_function = ch_delay->pre_change_function;
1448 ei->change->change_function = ch_delay->change_function;
1449 ei->change->post_change_function = ch_delay->post_change_function;
1451 ei->has_change_event[CE_DELAY] = TRUE;
1454 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1459 /* add change events from custom element configuration */
1460 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1462 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1464 for (j = 0; j < ei->num_change_pages; j++)
1466 if (!ei->change_page[j].can_change)
1469 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1471 /* only add event page for the first page found with this event */
1472 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1474 ei->has_change_event[k] = TRUE;
1476 ei->event_page_nr[k] = j;
1477 ei->event_page[k] = &ei->change_page[j];
1485 /* add change events from custom element configuration */
1486 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1488 int element = EL_CUSTOM_START + i;
1490 /* only add custom elements that change after fixed/random frame delay */
1491 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1492 element_info[element].has_change_event[CE_DELAY] = TRUE;
1496 /* ---------- initialize run-time trigger player and element ------------- */
1498 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1500 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1502 for (j = 0; j < ei->num_change_pages; j++)
1504 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1505 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1509 /* ---------- initialize trigger events ---------------------------------- */
1511 /* initialize trigger events information */
1512 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1513 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1514 trigger_events[i][j] = FALSE;
1517 /* add trigger events from element change event properties */
1518 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1520 struct ElementInfo *ei = &element_info[i];
1522 for (j = 0; j < ei->num_change_pages; j++)
1524 if (!ei->change_page[j].can_change)
1527 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1529 int trigger_element = ei->change_page[j].trigger_element;
1531 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1533 if (ei->change_page[j].has_event[k])
1535 if (IS_GROUP_ELEMENT(trigger_element))
1537 struct ElementGroupInfo *group =
1538 element_info[trigger_element].group;
1540 for (l = 0; l < group->num_elements_resolved; l++)
1541 trigger_events[group->element_resolved[l]][k] = TRUE;
1544 trigger_events[trigger_element][k] = TRUE;
1551 /* add trigger events from element change event properties */
1552 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1553 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1554 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1555 if (element_info[i].change->has_event[j])
1556 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1559 /* ---------- initialize push delay -------------------------------------- */
1561 /* initialize push delay values to default */
1562 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1564 if (!IS_CUSTOM_ELEMENT(i))
1566 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1567 element_info[i].push_delay_random = game.default_push_delay_random;
1571 /* set push delay value for certain elements from pre-defined list */
1572 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1574 int e = push_delay_list[i].element;
1576 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1577 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1580 /* set push delay value for Supaplex elements for newer engine versions */
1581 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1583 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1585 if (IS_SP_ELEMENT(i))
1587 #if USE_NEW_MOVE_STYLE
1588 /* set SP push delay to just enough to push under a falling zonk */
1589 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1591 element_info[i].push_delay_fixed = delay;
1592 element_info[i].push_delay_random = 0;
1594 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1595 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1601 /* ---------- initialize move stepsize ----------------------------------- */
1603 /* initialize move stepsize values to default */
1604 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1605 if (!IS_CUSTOM_ELEMENT(i))
1606 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1608 /* set move stepsize value for certain elements from pre-defined list */
1609 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1611 int e = move_stepsize_list[i].element;
1613 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1617 /* ---------- initialize move dig/leave ---------------------------------- */
1619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1621 element_info[i].can_leave_element = FALSE;
1622 element_info[i].can_leave_element_last = FALSE;
1626 /* ---------- initialize gem count --------------------------------------- */
1628 /* initialize gem count values for each element */
1629 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1630 if (!IS_CUSTOM_ELEMENT(i))
1631 element_info[i].collect_count = 0;
1633 /* add gem count values for all elements from pre-defined list */
1634 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1635 element_info[collect_count_list[i].element].collect_count =
1636 collect_count_list[i].count;
1638 /* ---------- initialize access direction -------------------------------- */
1640 /* initialize access direction values to default (access from every side) */
1641 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1642 if (!IS_CUSTOM_ELEMENT(i))
1643 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1645 /* set access direction value for certain elements from pre-defined list */
1646 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1647 element_info[access_direction_list[i].element].access_direction =
1648 access_direction_list[i].direction;
1653 =============================================================================
1655 -----------------------------------------------------------------------------
1656 initialize and start new game
1657 =============================================================================
1662 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1663 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1664 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1671 #if USE_NEW_AMOEBA_CODE
1672 printf("Using new amoeba code.\n");
1674 printf("Using old amoeba code.\n");
1679 /* don't play tapes over network */
1680 network_playing = (options.network && !tape.playing);
1682 for (i = 0; i < MAX_PLAYERS; i++)
1684 struct PlayerInfo *player = &stored_player[i];
1686 player->index_nr = i;
1687 player->index_bit = (1 << i);
1688 player->element_nr = EL_PLAYER_1 + i;
1690 player->present = FALSE;
1691 player->active = FALSE;
1694 player->effective_action = 0;
1695 player->programmed_action = 0;
1698 player->gems_still_needed = level.gems_needed;
1699 player->sokobanfields_still_needed = 0;
1700 player->lights_still_needed = 0;
1701 player->friends_still_needed = 0;
1703 for (j = 0; j < MAX_NUM_KEYS; j++)
1704 player->key[j] = FALSE;
1706 player->dynabomb_count = 0;
1707 player->dynabomb_size = 1;
1708 player->dynabombs_left = 0;
1709 player->dynabomb_xl = FALSE;
1711 player->MovDir = MV_NO_MOVING;
1714 player->GfxDir = MV_NO_MOVING;
1715 player->GfxAction = ACTION_DEFAULT;
1717 player->StepFrame = 0;
1719 player->use_murphy_graphic = FALSE;
1721 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1722 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1724 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1726 player->actual_frame_counter = 0;
1728 player->step_counter = 0;
1730 player->last_move_dir = MV_NO_MOVING;
1732 player->is_waiting = FALSE;
1733 player->is_moving = FALSE;
1734 player->is_auto_moving = FALSE;
1735 player->is_digging = FALSE;
1736 player->is_snapping = FALSE;
1737 player->is_collecting = FALSE;
1738 player->is_pushing = FALSE;
1739 player->is_switching = FALSE;
1740 player->is_dropping = FALSE;
1742 player->is_bored = FALSE;
1743 player->is_sleeping = FALSE;
1745 player->frame_counter_bored = -1;
1746 player->frame_counter_sleeping = -1;
1748 player->anim_delay_counter = 0;
1749 player->post_delay_counter = 0;
1751 player->action_waiting = ACTION_DEFAULT;
1752 player->last_action_waiting = ACTION_DEFAULT;
1753 player->special_action_bored = ACTION_DEFAULT;
1754 player->special_action_sleeping = ACTION_DEFAULT;
1756 player->num_special_action_bored = 0;
1757 player->num_special_action_sleeping = 0;
1759 /* determine number of special actions for bored and sleeping animation */
1760 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1762 boolean found = FALSE;
1764 for (k = 0; k < NUM_DIRECTIONS; k++)
1765 if (el_act_dir2img(player->element_nr, j, k) !=
1766 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1770 player->num_special_action_bored++;
1774 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1776 boolean found = FALSE;
1778 for (k = 0; k < NUM_DIRECTIONS; k++)
1779 if (el_act_dir2img(player->element_nr, j, k) !=
1780 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1784 player->num_special_action_sleeping++;
1789 player->switch_x = -1;
1790 player->switch_y = -1;
1793 player->drop_x = -1;
1794 player->drop_y = -1;
1797 player->show_envelope = 0;
1799 player->move_delay = game.initial_move_delay;
1800 player->move_delay_value = game.initial_move_delay_value;
1802 player->move_delay_reset_counter = 0;
1804 #if USE_NEW_PUSH_DELAY
1805 player->push_delay = -1; /* initialized when pushing starts */
1806 player->push_delay_value = game.initial_push_delay_value;
1808 player->push_delay = 0;
1809 player->push_delay_value = game.initial_push_delay_value;
1812 player->drop_delay = 0;
1814 player->last_jx = player->last_jy = 0;
1815 player->jx = player->jy = 0;
1817 player->shield_normal_time_left = 0;
1818 player->shield_deadly_time_left = 0;
1820 player->inventory_infinite_element = EL_UNDEFINED;
1821 player->inventory_size = 0;
1823 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1824 SnapField(player, 0, 0);
1826 player->LevelSolved = FALSE;
1827 player->GameOver = FALSE;
1830 network_player_action_received = FALSE;
1832 #if defined(NETWORK_AVALIABLE)
1833 /* initial null action */
1834 if (network_playing)
1835 SendToServer_MovePlayer(MV_NO_MOVING);
1844 TimeLeft = level.time;
1847 ScreenMovDir = MV_NO_MOVING;
1851 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1853 AllPlayersGone = FALSE;
1855 game.yamyam_content_nr = 0;
1856 game.magic_wall_active = FALSE;
1857 game.magic_wall_time_left = 0;
1858 game.light_time_left = 0;
1859 game.timegate_time_left = 0;
1860 game.switchgate_pos = 0;
1861 game.balloon_dir = MV_NO_MOVING;
1862 game.gravity = level.initial_gravity;
1863 game.explosions_delayed = TRUE;
1865 game.envelope_active = FALSE;
1867 for (i = 0; i < NUM_BELTS; i++)
1869 game.belt_dir[i] = MV_NO_MOVING;
1870 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1873 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1874 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1876 for (x = 0; x < lev_fieldx; x++)
1878 for (y = 0; y < lev_fieldy; y++)
1880 Feld[x][y] = level.field[x][y];
1881 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1882 ChangeDelay[x][y] = 0;
1883 ChangePage[x][y] = -1;
1884 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1886 WasJustMoving[x][y] = 0;
1887 WasJustFalling[x][y] = 0;
1888 CheckCollision[x][y] = 0;
1890 Pushed[x][y] = FALSE;
1892 Changed[x][y] = FALSE;
1893 ChangeEvent[x][y] = -1;
1895 ExplodePhase[x][y] = 0;
1896 ExplodeDelay[x][y] = 0;
1897 ExplodeField[x][y] = EX_TYPE_NONE;
1899 RunnerVisit[x][y] = 0;
1900 PlayerVisit[x][y] = 0;
1903 GfxRandom[x][y] = INIT_GFX_RANDOM();
1904 GfxElement[x][y] = EL_UNDEFINED;
1905 GfxAction[x][y] = ACTION_DEFAULT;
1906 GfxDir[x][y] = MV_NO_MOVING;
1910 for (y = 0; y < lev_fieldy; y++)
1912 for (x = 0; x < lev_fieldx; x++)
1914 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1916 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1918 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1921 InitField(x, y, TRUE);
1927 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1928 emulate_sb ? EMU_SOKOBAN :
1929 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1931 /* initialize explosion and ignition delay */
1932 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1934 if (!IS_CUSTOM_ELEMENT(i))
1937 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1938 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1939 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1940 int last_phase = (num_phase + 1) * delay;
1941 int half_phase = (num_phase / 2) * delay;
1943 element_info[i].explosion_delay = last_phase - 1;
1944 element_info[i].ignition_delay = half_phase;
1947 if (i == EL_BLACK_ORB)
1948 element_info[i].ignition_delay = 0;
1950 if (i == EL_BLACK_ORB)
1951 element_info[i].ignition_delay = 1;
1956 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1957 element_info[i].explosion_delay = 1;
1959 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1960 element_info[i].ignition_delay = 1;
1964 /* correct non-moving belts to start moving left */
1965 for (i = 0; i < NUM_BELTS; i++)
1966 if (game.belt_dir[i] == MV_NO_MOVING)
1967 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1969 /* check if any connected player was not found in playfield */
1970 for (i = 0; i < MAX_PLAYERS; i++)
1972 struct PlayerInfo *player = &stored_player[i];
1974 if (player->connected && !player->present)
1976 for (j = 0; j < MAX_PLAYERS; j++)
1978 struct PlayerInfo *some_player = &stored_player[j];
1979 int jx = some_player->jx, jy = some_player->jy;
1981 /* assign first free player found that is present in the playfield */
1982 if (some_player->present && !some_player->connected)
1984 player->present = TRUE;
1985 player->active = TRUE;
1987 some_player->present = FALSE;
1988 some_player->active = FALSE;
1991 player->element_nr = some_player->element_nr;
1994 #if USE_NEW_BLOCK_STYLE
1995 player->block_last_field = some_player->block_last_field;
1996 player->block_delay_adjustment = some_player->block_delay_adjustment;
1999 StorePlayer[jx][jy] = player->element_nr;
2000 player->jx = player->last_jx = jx;
2001 player->jy = player->last_jy = jy;
2011 /* when playing a tape, eliminate all players which do not participate */
2013 for (i = 0; i < MAX_PLAYERS; i++)
2015 if (stored_player[i].active && !tape.player_participates[i])
2017 struct PlayerInfo *player = &stored_player[i];
2018 int jx = player->jx, jy = player->jy;
2020 player->active = FALSE;
2021 StorePlayer[jx][jy] = 0;
2022 Feld[jx][jy] = EL_EMPTY;
2026 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2028 /* when in single player mode, eliminate all but the first active player */
2030 for (i = 0; i < MAX_PLAYERS; i++)
2032 if (stored_player[i].active)
2034 for (j = i + 1; j < MAX_PLAYERS; j++)
2036 if (stored_player[j].active)
2038 struct PlayerInfo *player = &stored_player[j];
2039 int jx = player->jx, jy = player->jy;
2041 player->active = FALSE;
2042 player->present = FALSE;
2044 StorePlayer[jx][jy] = 0;
2045 Feld[jx][jy] = EL_EMPTY;
2052 /* when recording the game, store which players take part in the game */
2055 for (i = 0; i < MAX_PLAYERS; i++)
2056 if (stored_player[i].active)
2057 tape.player_participates[i] = TRUE;
2062 for (i = 0; i < MAX_PLAYERS; i++)
2064 struct PlayerInfo *player = &stored_player[i];
2066 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2071 if (local_player == player)
2072 printf("Player %d is local player.\n", i+1);
2076 if (BorderElement == EL_EMPTY)
2079 SBX_Right = lev_fieldx - SCR_FIELDX;
2081 SBY_Lower = lev_fieldy - SCR_FIELDY;
2086 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2088 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2091 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2092 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2094 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2095 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2097 /* if local player not found, look for custom element that might create
2098 the player (make some assumptions about the right custom element) */
2099 if (!local_player->present)
2101 int start_x = 0, start_y = 0;
2102 int found_rating = 0;
2103 int found_element = EL_UNDEFINED;
2105 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2107 int element = Feld[x][y];
2112 if (!IS_CUSTOM_ELEMENT(element))
2115 if (CAN_CHANGE(element))
2117 for (i = 0; i < element_info[element].num_change_pages; i++)
2119 content = element_info[element].change_page[i].target_element;
2120 is_player = ELEM_IS_PLAYER(content);
2122 if (is_player && (found_rating < 3 || element < found_element))
2128 found_element = element;
2133 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2135 content = element_info[element].content[xx][yy];
2136 is_player = ELEM_IS_PLAYER(content);
2138 if (is_player && (found_rating < 2 || element < found_element))
2140 start_x = x + xx - 1;
2141 start_y = y + yy - 1;
2144 found_element = element;
2147 if (!CAN_CHANGE(element))
2150 for (i = 0; i < element_info[element].num_change_pages; i++)
2152 content= element_info[element].change_page[i].target_content[xx][yy];
2153 is_player = ELEM_IS_PLAYER(content);
2155 if (is_player && (found_rating < 1 || element < found_element))
2157 start_x = x + xx - 1;
2158 start_y = y + yy - 1;
2161 found_element = element;
2167 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2168 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2171 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2172 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2178 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2179 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2180 local_player->jx - MIDPOSX);
2182 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2183 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2184 local_player->jy - MIDPOSY);
2186 scroll_x = SBX_Left;
2187 scroll_y = SBY_Upper;
2188 if (local_player->jx >= SBX_Left + MIDPOSX)
2189 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2190 local_player->jx - MIDPOSX :
2192 if (local_player->jy >= SBY_Upper + MIDPOSY)
2193 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2194 local_player->jy - MIDPOSY :
2199 CloseDoor(DOOR_CLOSE_1);
2201 /* !!! FIX THIS (START) !!! */
2202 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2204 InitGameEngine_EM();
2211 /* after drawing the level, correct some elements */
2212 if (game.timegate_time_left == 0)
2213 CloseAllOpenTimegates();
2215 if (setup.soft_scrolling)
2216 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2218 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2221 /* !!! FIX THIS (END) !!! */
2223 /* copy default game door content to main double buffer */
2224 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2225 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2227 DrawGameDoorValues();
2231 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2232 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2233 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2237 /* copy actual game door content to door double buffer for OpenDoor() */
2238 BlitBitmap(drawto, bitmap_db_door,
2239 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2241 OpenDoor(DOOR_OPEN_ALL);
2243 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2245 if (setup.sound_music)
2248 KeyboardAutoRepeatOffUnlessAutoplay();
2252 for (i = 0; i < MAX_PLAYERS; i++)
2253 printf("Player %d %sactive.\n",
2254 i + 1, (stored_player[i].active ? "" : "not "));
2258 printf("::: starting game [%d]\n", FrameCounter);
2262 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2264 /* this is used for non-R'n'D game engines to update certain engine values */
2266 /* needed to determine if sounds are played within the visible screen area */
2267 scroll_x = actual_scroll_x;
2268 scroll_y = actual_scroll_y;
2271 void InitMovDir(int x, int y)
2273 int i, element = Feld[x][y];
2274 static int xy[4][2] =
2281 static int direction[3][4] =
2283 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2284 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2285 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2294 Feld[x][y] = EL_BUG;
2295 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2298 case EL_SPACESHIP_RIGHT:
2299 case EL_SPACESHIP_UP:
2300 case EL_SPACESHIP_LEFT:
2301 case EL_SPACESHIP_DOWN:
2302 Feld[x][y] = EL_SPACESHIP;
2303 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2306 case EL_BD_BUTTERFLY_RIGHT:
2307 case EL_BD_BUTTERFLY_UP:
2308 case EL_BD_BUTTERFLY_LEFT:
2309 case EL_BD_BUTTERFLY_DOWN:
2310 Feld[x][y] = EL_BD_BUTTERFLY;
2311 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2314 case EL_BD_FIREFLY_RIGHT:
2315 case EL_BD_FIREFLY_UP:
2316 case EL_BD_FIREFLY_LEFT:
2317 case EL_BD_FIREFLY_DOWN:
2318 Feld[x][y] = EL_BD_FIREFLY;
2319 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2322 case EL_PACMAN_RIGHT:
2324 case EL_PACMAN_LEFT:
2325 case EL_PACMAN_DOWN:
2326 Feld[x][y] = EL_PACMAN;
2327 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2330 case EL_SP_SNIKSNAK:
2331 MovDir[x][y] = MV_UP;
2334 case EL_SP_ELECTRON:
2335 MovDir[x][y] = MV_LEFT;
2342 Feld[x][y] = EL_MOLE;
2343 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2347 if (IS_CUSTOM_ELEMENT(element))
2349 struct ElementInfo *ei = &element_info[element];
2350 int move_direction_initial = ei->move_direction_initial;
2351 int move_pattern = ei->move_pattern;
2353 if (move_direction_initial == MV_START_PREVIOUS)
2355 if (MovDir[x][y] != MV_NO_MOVING)
2358 move_direction_initial = MV_START_AUTOMATIC;
2361 if (move_direction_initial == MV_START_RANDOM)
2362 MovDir[x][y] = 1 << RND(4);
2363 else if (move_direction_initial & MV_ANY_DIRECTION)
2364 MovDir[x][y] = move_direction_initial;
2365 else if (move_pattern == MV_ALL_DIRECTIONS ||
2366 move_pattern == MV_TURNING_LEFT ||
2367 move_pattern == MV_TURNING_RIGHT ||
2368 move_pattern == MV_TURNING_LEFT_RIGHT ||
2369 move_pattern == MV_TURNING_RIGHT_LEFT ||
2370 move_pattern == MV_TURNING_RANDOM)
2371 MovDir[x][y] = 1 << RND(4);
2372 else if (move_pattern == MV_HORIZONTAL)
2373 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2374 else if (move_pattern == MV_VERTICAL)
2375 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2376 else if (move_pattern & MV_ANY_DIRECTION)
2377 MovDir[x][y] = element_info[element].move_pattern;
2378 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2379 move_pattern == MV_ALONG_RIGHT_SIDE)
2382 /* use random direction as default start direction */
2383 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2384 MovDir[x][y] = 1 << RND(4);
2387 for (i = 0; i < NUM_DIRECTIONS; i++)
2389 int x1 = x + xy[i][0];
2390 int y1 = y + xy[i][1];
2392 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2394 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2395 MovDir[x][y] = direction[0][i];
2397 MovDir[x][y] = direction[1][i];
2406 MovDir[x][y] = 1 << RND(4);
2408 if (element != EL_BUG &&
2409 element != EL_SPACESHIP &&
2410 element != EL_BD_BUTTERFLY &&
2411 element != EL_BD_FIREFLY)
2414 for (i = 0; i < NUM_DIRECTIONS; i++)
2416 int x1 = x + xy[i][0];
2417 int y1 = y + xy[i][1];
2419 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2421 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2423 MovDir[x][y] = direction[0][i];
2426 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2427 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2429 MovDir[x][y] = direction[1][i];
2438 GfxDir[x][y] = MovDir[x][y];
2441 void InitAmoebaNr(int x, int y)
2444 int group_nr = AmoebeNachbarNr(x, y);
2448 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2450 if (AmoebaCnt[i] == 0)
2458 AmoebaNr[x][y] = group_nr;
2459 AmoebaCnt[group_nr]++;
2460 AmoebaCnt2[group_nr]++;
2466 boolean raise_level = FALSE;
2468 if (local_player->MovPos)
2472 if (tape.auto_play) /* tape might already be stopped here */
2473 tape.auto_play_level_solved = TRUE;
2475 if (tape.playing && tape.auto_play)
2476 tape.auto_play_level_solved = TRUE;
2479 local_player->LevelSolved = FALSE;
2481 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2485 if (!tape.playing && setup.sound_loops)
2486 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2487 SND_CTRL_PLAY_LOOP);
2489 while (TimeLeft > 0)
2491 if (!tape.playing && !setup.sound_loops)
2492 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2493 if (TimeLeft > 0 && !(TimeLeft % 10))
2494 RaiseScore(level.score[SC_TIME_BONUS]);
2495 if (TimeLeft > 100 && !(TimeLeft % 10))
2500 DrawGameValue_Time(TimeLeft);
2508 if (!tape.playing && setup.sound_loops)
2509 StopSound(SND_GAME_LEVELTIME_BONUS);
2511 else if (level.time == 0) /* level without time limit */
2513 if (!tape.playing && setup.sound_loops)
2514 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2515 SND_CTRL_PLAY_LOOP);
2517 while (TimePlayed < 999)
2519 if (!tape.playing && !setup.sound_loops)
2520 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2521 if (TimePlayed < 999 && !(TimePlayed % 10))
2522 RaiseScore(level.score[SC_TIME_BONUS]);
2523 if (TimePlayed < 900 && !(TimePlayed % 10))
2528 DrawGameValue_Time(TimePlayed);
2536 if (!tape.playing && setup.sound_loops)
2537 StopSound(SND_GAME_LEVELTIME_BONUS);
2540 /* close exit door after last player */
2541 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2542 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2543 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2545 int element = Feld[ExitX][ExitY];
2547 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2548 EL_SP_EXIT_CLOSING);
2550 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2553 /* Hero disappears */
2554 if (ExitX >= 0 && ExitY >= 0)
2555 DrawLevelField(ExitX, ExitY);
2562 CloseDoor(DOOR_CLOSE_1);
2567 SaveTape(tape.level_nr); /* Ask to save tape */
2570 if (level_nr == leveldir_current->handicap_level)
2572 leveldir_current->handicap_level++;
2573 SaveLevelSetup_SeriesInfo();
2576 if (level_editor_test_game)
2577 local_player->score = -1; /* no highscore when playing from editor */
2578 else if (level_nr < leveldir_current->last_level)
2579 raise_level = TRUE; /* advance to next level */
2581 if ((hi_pos = NewHiScore()) >= 0)
2583 game_status = GAME_MODE_SCORES;
2584 DrawHallOfFame(hi_pos);
2593 game_status = GAME_MODE_MAIN;
2610 LoadScore(level_nr);
2612 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2613 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2616 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2618 if (local_player->score > highscore[k].Score)
2620 /* player has made it to the hall of fame */
2622 if (k < MAX_SCORE_ENTRIES - 1)
2624 int m = MAX_SCORE_ENTRIES - 1;
2627 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2628 if (!strcmp(setup.player_name, highscore[l].Name))
2630 if (m == k) /* player's new highscore overwrites his old one */
2634 for (l = m; l > k; l--)
2636 strcpy(highscore[l].Name, highscore[l - 1].Name);
2637 highscore[l].Score = highscore[l - 1].Score;
2644 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2645 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2646 highscore[k].Score = local_player->score;
2652 else if (!strncmp(setup.player_name, highscore[k].Name,
2653 MAX_PLAYER_NAME_LEN))
2654 break; /* player already there with a higher score */
2660 SaveScore(level_nr);
2665 inline static int getElementMoveStepsize(int x, int y)
2667 int element = Feld[x][y];
2668 int direction = MovDir[x][y];
2669 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2670 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2671 int horiz_move = (dx != 0);
2672 int sign = (horiz_move ? dx : dy);
2673 int step = sign * element_info[element].move_stepsize;
2675 /* special values for move stepsize for spring and things on conveyor belt */
2679 if (element == EL_SPRING)
2680 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2681 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2682 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2683 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2685 if (CAN_FALL(element) &&
2686 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2687 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2688 else if (element == EL_SPRING)
2689 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2696 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2698 if (player->GfxAction != action || player->GfxDir != dir)
2701 printf("Player frame reset! (%d => %d, %d => %d)\n",
2702 player->GfxAction, action, player->GfxDir, dir);
2705 player->GfxAction = action;
2706 player->GfxDir = dir;
2708 player->StepFrame = 0;
2712 static void ResetRandomAnimationValue(int x, int y)
2714 GfxRandom[x][y] = INIT_GFX_RANDOM();
2717 static void ResetGfxAnimation(int x, int y)
2720 GfxAction[x][y] = ACTION_DEFAULT;
2721 GfxDir[x][y] = MovDir[x][y];
2724 void InitMovingField(int x, int y, int direction)
2726 int element = Feld[x][y];
2727 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2728 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2732 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2733 ResetGfxAnimation(x, y);
2735 #if USE_CAN_MOVE_NOT_MOVING
2737 MovDir[x][y] = direction;
2738 GfxDir[x][y] = direction;
2739 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2740 ACTION_FALLING : ACTION_MOVING);
2742 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2744 if (Feld[newx][newy] == EL_EMPTY)
2745 Feld[newx][newy] = EL_BLOCKED;
2747 MovDir[newx][newy] = MovDir[x][y];
2748 GfxFrame[newx][newy] = GfxFrame[x][y];
2749 GfxRandom[newx][newy] = GfxRandom[x][y];
2750 GfxAction[newx][newy] = GfxAction[x][y];
2751 GfxDir[newx][newy] = GfxDir[x][y];
2756 MovDir[newx][newy] = MovDir[x][y] = direction;
2757 GfxDir[x][y] = direction;
2759 if (Feld[newx][newy] == EL_EMPTY)
2760 Feld[newx][newy] = EL_BLOCKED;
2762 if (direction == MV_DOWN && CAN_FALL(element))
2763 GfxAction[x][y] = ACTION_FALLING;
2765 GfxAction[x][y] = ACTION_MOVING;
2767 GfxFrame[newx][newy] = GfxFrame[x][y];
2768 GfxRandom[newx][newy] = GfxRandom[x][y];
2769 GfxAction[newx][newy] = GfxAction[x][y];
2770 GfxDir[newx][newy] = GfxDir[x][y];
2774 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2776 int direction = MovDir[x][y];
2777 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2778 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2784 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2786 int oldx = x, oldy = y;
2787 int direction = MovDir[x][y];
2789 if (direction == MV_LEFT)
2791 else if (direction == MV_RIGHT)
2793 else if (direction == MV_UP)
2795 else if (direction == MV_DOWN)
2798 *comes_from_x = oldx;
2799 *comes_from_y = oldy;
2802 int MovingOrBlocked2Element(int x, int y)
2804 int element = Feld[x][y];
2806 if (element == EL_BLOCKED)
2810 Blocked2Moving(x, y, &oldx, &oldy);
2811 return Feld[oldx][oldy];
2817 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2819 /* like MovingOrBlocked2Element(), but if element is moving
2820 and (x,y) is the field the moving element is just leaving,
2821 return EL_BLOCKED instead of the element value */
2822 int element = Feld[x][y];
2824 if (IS_MOVING(x, y))
2826 if (element == EL_BLOCKED)
2830 Blocked2Moving(x, y, &oldx, &oldy);
2831 return Feld[oldx][oldy];
2840 static void RemoveField(int x, int y)
2842 Feld[x][y] = EL_EMPTY;
2849 ChangeDelay[x][y] = 0;
2850 ChangePage[x][y] = -1;
2851 Pushed[x][y] = FALSE;
2854 ExplodeField[x][y] = EX_TYPE_NONE;
2857 GfxElement[x][y] = EL_UNDEFINED;
2858 GfxAction[x][y] = ACTION_DEFAULT;
2859 GfxDir[x][y] = MV_NO_MOVING;
2862 void RemoveMovingField(int x, int y)
2864 int oldx = x, oldy = y, newx = x, newy = y;
2865 int element = Feld[x][y];
2866 int next_element = EL_UNDEFINED;
2868 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2871 if (IS_MOVING(x, y))
2873 Moving2Blocked(x, y, &newx, &newy);
2875 if (Feld[newx][newy] != EL_BLOCKED)
2878 if (Feld[newx][newy] != EL_BLOCKED)
2880 /* element is moving, but target field is not free (blocked), but
2881 already occupied by something different (example: acid pool);
2882 in this case, only remove the moving field, but not the target */
2884 RemoveField(oldx, oldy);
2886 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2888 DrawLevelField(oldx, oldy);
2894 else if (element == EL_BLOCKED)
2896 Blocked2Moving(x, y, &oldx, &oldy);
2897 if (!IS_MOVING(oldx, oldy))
2901 if (element == EL_BLOCKED &&
2902 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2903 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2904 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2905 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2906 next_element = get_next_element(Feld[oldx][oldy]);
2908 RemoveField(oldx, oldy);
2909 RemoveField(newx, newy);
2911 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2913 if (next_element != EL_UNDEFINED)
2914 Feld[oldx][oldy] = next_element;
2916 DrawLevelField(oldx, oldy);
2917 DrawLevelField(newx, newy);
2920 void DrawDynamite(int x, int y)
2922 int sx = SCREENX(x), sy = SCREENY(y);
2923 int graphic = el2img(Feld[x][y]);
2926 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2929 if (IS_WALKABLE_INSIDE(Back[x][y]))
2933 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2934 else if (Store[x][y])
2935 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2937 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2940 if (Back[x][y] || Store[x][y])
2941 DrawGraphicThruMask(sx, sy, graphic, frame);
2943 DrawGraphic(sx, sy, graphic, frame);
2945 if (game.emulation == EMU_SUPAPLEX)
2946 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2947 else if (Store[x][y])
2948 DrawGraphicThruMask(sx, sy, graphic, frame);
2950 DrawGraphic(sx, sy, graphic, frame);
2954 void CheckDynamite(int x, int y)
2956 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2960 if (MovDelay[x][y] != 0)
2963 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2970 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2972 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2973 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2974 StopSound(SND_DYNAMITE_ACTIVE);
2976 StopSound(SND_DYNABOMB_ACTIVE);
2982 void DrawRelocatePlayer(struct PlayerInfo *player)
2984 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2985 boolean no_delay = (tape.warp_forward);
2986 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2987 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2988 int jx = player->jx;
2989 int jy = player->jy;
2991 if (level.instant_relocation)
2994 int offset = (setup.scroll_delay ? 3 : 0);
2996 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2998 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2999 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3000 local_player->jx - MIDPOSX);
3002 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3003 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3004 local_player->jy - MIDPOSY);
3008 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3009 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3010 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3012 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3013 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3014 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3016 /* don't scroll over playfield boundaries */
3017 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3018 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3020 /* don't scroll over playfield boundaries */
3021 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3022 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3025 scroll_x += (local_player->jx - old_jx);
3026 scroll_y += (local_player->jy - old_jy);
3028 /* don't scroll over playfield boundaries */
3029 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3030 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3032 /* don't scroll over playfield boundaries */
3033 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3034 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3037 RedrawPlayfield(TRUE, 0,0,0,0);
3043 int offset = (setup.scroll_delay ? 3 : 0);
3045 int scroll_xx = -999, scroll_yy = -999;
3047 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3049 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3052 int fx = FX, fy = FY;
3054 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3055 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3056 local_player->jx - MIDPOSX);
3058 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3059 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3060 local_player->jy - MIDPOSY);
3062 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3063 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3066 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3069 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3076 fx += dx * TILEX / 2;
3077 fy += dy * TILEY / 2;
3079 ScrollLevel(dx, dy);
3082 /* scroll in two steps of half tile size to make things smoother */
3083 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3085 Delay(wait_delay_value);
3087 /* scroll second step to align at full tile size */
3089 Delay(wait_delay_value);
3092 int scroll_xx = -999, scroll_yy = -999;
3094 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3096 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3099 int fx = FX, fy = FY;
3101 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3102 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3103 local_player->jx - MIDPOSX);
3105 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3106 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3107 local_player->jy - MIDPOSY);
3109 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3110 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3113 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3116 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3123 fx += dx * TILEX / 2;
3124 fy += dy * TILEY / 2;
3126 ScrollLevel(dx, dy);
3129 /* scroll in two steps of half tile size to make things smoother */
3130 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3132 Delay(wait_delay_value);
3134 /* scroll second step to align at full tile size */
3136 Delay(wait_delay_value);
3142 Delay(wait_delay_value);
3146 void RelocatePlayer(int jx, int jy, int el_player_raw)
3149 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3151 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3153 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3154 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3155 boolean no_delay = (tape.warp_forward);
3156 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3157 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3158 int old_jx = player->jx;
3159 int old_jy = player->jy;
3160 int old_element = Feld[old_jx][old_jy];
3161 int element = Feld[jx][jy];
3162 boolean player_relocated = (old_jx != jx || old_jy != jy);
3164 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3165 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3167 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3168 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3169 int leave_side_horiz = move_dir_horiz;
3170 int leave_side_vert = move_dir_vert;
3172 static int trigger_sides[4][2] =
3174 /* enter side leave side */
3175 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3176 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3177 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3178 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3180 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3181 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3182 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3183 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3185 int enter_side = enter_side_horiz | enter_side_vert;
3186 int leave_side = leave_side_horiz | leave_side_vert;
3188 if (player->GameOver) /* do not reanimate dead player */
3191 if (!player_relocated) /* no need to relocate the player */
3194 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3196 RemoveField(jx, jy); /* temporarily remove newly placed player */
3197 DrawLevelField(jx, jy);
3200 if (player->present)
3202 while (player->MovPos)
3204 ScrollPlayer(player, SCROLL_GO_ON);
3205 ScrollScreen(NULL, SCROLL_GO_ON);
3207 #if USE_NEW_MOVE_DELAY
3208 AdvanceFrameAndPlayerCounters(player->index_nr);
3216 Delay(wait_delay_value);
3219 DrawPlayer(player); /* needed here only to cleanup last field */
3220 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3222 player->is_moving = FALSE;
3226 if (IS_CUSTOM_ELEMENT(old_element))
3227 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3229 player->index_bit, leave_side);
3231 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3233 player->index_bit, leave_side);
3236 Feld[jx][jy] = el_player;
3237 InitPlayerField(jx, jy, el_player, TRUE);
3239 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3241 Feld[jx][jy] = element;
3242 InitField(jx, jy, FALSE);
3246 if (player == local_player) /* only visually relocate local player */
3247 DrawRelocatePlayer(player);
3251 TestIfHeroTouchesBadThing(jx, jy);
3252 TestIfPlayerTouchesCustomElement(jx, jy);
3256 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3261 /* needed to allow change of walkable custom element by entering player */
3262 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3263 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3265 /* needed to allow change of walkable custom element by entering player */
3266 Changed[jx][jy] = 0; /* allow another change */
3271 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3272 enter_side == MV_LEFT ? "left" :
3273 enter_side == MV_RIGHT ? "right" :
3274 enter_side == MV_UP ? "top" :
3275 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3279 if (IS_CUSTOM_ELEMENT(element))
3280 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3281 player->index_bit, enter_side);
3283 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3285 player->index_bit, enter_side);
3289 void Explode(int ex, int ey, int phase, int mode)
3296 /* !!! eliminate this variable !!! */
3297 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3302 int last_phase = num_phase * delay;
3303 int half_phase = (num_phase / 2) * delay;
3304 int first_phase_after_start = EX_PHASE_START + 1;
3308 if (game.explosions_delayed)
3310 ExplodeField[ex][ey] = mode;
3314 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3316 int center_element = Feld[ex][ey];
3319 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3323 /* --- This is only really needed (and now handled) in "Impact()". --- */
3324 /* do not explode moving elements that left the explode field in time */
3325 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3326 center_element == EL_EMPTY &&
3327 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3332 if (mode == EX_TYPE_NORMAL ||
3333 mode == EX_TYPE_CENTER ||
3334 mode == EX_TYPE_CROSS)
3335 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3337 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3338 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3341 /* remove things displayed in background while burning dynamite */
3342 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3345 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3347 /* put moving element to center field (and let it explode there) */
3348 center_element = MovingOrBlocked2Element(ex, ey);
3349 RemoveMovingField(ex, ey);
3350 Feld[ex][ey] = center_element;
3356 last_phase = element_info[center_element].explosion_delay + 1;
3358 last_phase = element_info[center_element].explosion_delay;
3362 printf("::: %d -> %d\n", center_element, last_phase);
3366 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3368 int xx = x - ex + 1;
3369 int yy = y - ey + 1;
3374 if (!IN_LEV_FIELD(x, y) ||
3375 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3376 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3379 if (!IN_LEV_FIELD(x, y) ||
3380 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3384 if (!IN_LEV_FIELD(x, y) ||
3385 ((mode != EX_TYPE_NORMAL ||
3386 center_element == EL_AMOEBA_TO_DIAMOND) &&
3387 (x != ex || y != ey)))
3391 element = Feld[x][y];
3393 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3395 element = MovingOrBlocked2Element(x, y);
3397 if (!IS_EXPLOSION_PROOF(element))
3398 RemoveMovingField(x, y);
3404 if (IS_EXPLOSION_PROOF(element))
3407 /* indestructible elements can only explode in center (but not flames) */
3409 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3410 mode == EX_TYPE_BORDER)) ||
3411 element == EL_FLAMES)
3414 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3415 element == EL_FLAMES)
3421 if ((IS_INDESTRUCTIBLE(element) &&
3422 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3423 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3424 element == EL_FLAMES)
3428 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3429 behaviour, for example when touching a yamyam that explodes to rocks
3430 with active deadly shield, a rock is created under the player !!! */
3431 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3433 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3434 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3435 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3437 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3440 if (IS_ACTIVE_BOMB(element))
3442 /* re-activate things under the bomb like gate or penguin */
3444 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3447 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3452 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3453 element_info[Feld[x][y]].token_name,
3454 Store[x][y], Store2[x][y]);
3461 /* save walkable background elements while explosion on same tile */
3463 if (IS_INDESTRUCTIBLE(element))
3464 Back[x][y] = element;
3468 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3469 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3470 Back[x][y] = element;
3472 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3473 (x != ex || y != ey))
3474 Back[x][y] = element;
3477 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3478 Back[x][y] = element;
3482 /* ignite explodable elements reached by other explosion */
3483 if (element == EL_EXPLOSION)
3484 element = Store2[x][y];
3487 if (AmoebaNr[x][y] &&
3488 (element == EL_AMOEBA_FULL ||
3489 element == EL_BD_AMOEBA ||
3490 element == EL_AMOEBA_GROWING))
3492 AmoebaCnt[AmoebaNr[x][y]]--;
3493 AmoebaCnt2[AmoebaNr[x][y]]--;
3499 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3501 switch(StorePlayer[ex][ey])
3504 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3507 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3510 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3514 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3519 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3520 Store[x][y] = EL_EMPTY;
3522 if (game.emulation == EMU_SUPAPLEX)
3523 Store[x][y] = EL_EMPTY;
3526 else if (center_element == EL_MOLE)
3527 Store[x][y] = EL_EMERALD_RED;
3528 else if (center_element == EL_PENGUIN)
3529 Store[x][y] = EL_EMERALD_PURPLE;
3530 else if (center_element == EL_BUG)
3531 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3532 else if (center_element == EL_BD_BUTTERFLY)
3533 Store[x][y] = EL_BD_DIAMOND;
3534 else if (center_element == EL_SP_ELECTRON)
3535 Store[x][y] = EL_SP_INFOTRON;
3536 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3537 Store[x][y] = level.amoeba_content;
3538 else if (center_element == EL_YAMYAM)
3539 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3540 else if (IS_CUSTOM_ELEMENT(center_element) &&
3541 element_info[center_element].content[xx][yy] != EL_EMPTY)
3542 Store[x][y] = element_info[center_element].content[xx][yy];
3543 else if (element == EL_WALL_EMERALD)
3544 Store[x][y] = EL_EMERALD;
3545 else if (element == EL_WALL_DIAMOND)
3546 Store[x][y] = EL_DIAMOND;
3547 else if (element == EL_WALL_BD_DIAMOND)
3548 Store[x][y] = EL_BD_DIAMOND;
3549 else if (element == EL_WALL_EMERALD_YELLOW)
3550 Store[x][y] = EL_EMERALD_YELLOW;
3551 else if (element == EL_WALL_EMERALD_RED)
3552 Store[x][y] = EL_EMERALD_RED;
3553 else if (element == EL_WALL_EMERALD_PURPLE)
3554 Store[x][y] = EL_EMERALD_PURPLE;
3555 else if (element == EL_WALL_PEARL)
3556 Store[x][y] = EL_PEARL;
3557 else if (element == EL_WALL_CRYSTAL)
3558 Store[x][y] = EL_CRYSTAL;
3559 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3560 Store[x][y] = element_info[element].content[1][1];
3562 Store[x][y] = EL_EMPTY;
3564 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3565 center_element == EL_AMOEBA_TO_DIAMOND)
3566 Store2[x][y] = element;
3569 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3570 element_info[Store2[x][y]].token_name);
3574 if (AmoebaNr[x][y] &&
3575 (element == EL_AMOEBA_FULL ||
3576 element == EL_BD_AMOEBA ||
3577 element == EL_AMOEBA_GROWING))
3579 AmoebaCnt[AmoebaNr[x][y]]--;
3580 AmoebaCnt2[AmoebaNr[x][y]]--;
3586 MovDir[x][y] = MovPos[x][y] = 0;
3587 GfxDir[x][y] = MovDir[x][y];
3592 Feld[x][y] = EL_EXPLOSION;
3594 GfxElement[x][y] = center_element;
3596 GfxElement[x][y] = EL_UNDEFINED;
3599 ExplodePhase[x][y] = 1;
3601 ExplodeDelay[x][y] = last_phase;
3606 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3608 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3615 if (center_element == EL_YAMYAM)
3616 game.yamyam_content_nr =
3617 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3620 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3621 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3635 GfxFrame[x][y] = 0; /* restart explosion animation */
3639 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3643 last_phase = ExplodeDelay[x][y];
3646 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3650 /* activate this even in non-DEBUG version until cause for crash in
3651 getGraphicAnimationFrame() (see below) is found and eliminated */
3655 if (GfxElement[x][y] == EL_UNDEFINED)
3658 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3659 printf("Explode(): This should never happen!\n");
3662 GfxElement[x][y] = EL_EMPTY;
3668 border_element = Store2[x][y];
3670 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3671 border_element = StorePlayer[x][y];
3673 if (IS_PLAYER(x, y))
3674 border_element = StorePlayer[x][y];
3678 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3679 element_info[border_element].token_name, Store2[x][y]);
3683 printf("::: phase == %d\n", phase);
3686 if (phase == element_info[border_element].ignition_delay ||
3687 phase == last_phase)
3689 boolean border_explosion = FALSE;
3693 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3694 !PLAYER_EXPLOSION_PROTECTED(x, y))
3696 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3699 if (IS_PLAYER(x, y))
3702 KillHeroUnlessExplosionProtected(x, y);
3703 border_explosion = TRUE;
3706 if (phase == last_phase)
3707 printf("::: IS_PLAYER\n");
3710 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3713 printf("::: %d,%d: %d %s\n", x, y, border_element,
3714 element_info[border_element].token_name);
3717 Feld[x][y] = Store2[x][y];
3720 border_explosion = TRUE;
3723 if (phase == last_phase)
3724 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3727 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3729 AmoebeUmwandeln(x, y);
3731 border_explosion = TRUE;
3734 if (phase == last_phase)
3735 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3736 element_info[border_element].explosion_delay,
3737 element_info[border_element].ignition_delay,
3743 /* if an element just explodes due to another explosion (chain-reaction),
3744 do not immediately end the new explosion when it was the last frame of
3745 the explosion (as it would be done in the following "if"-statement!) */
3746 if (border_explosion && phase == last_phase)
3753 if (phase == first_phase_after_start)
3755 int element = Store2[x][y];
3757 if (element == EL_BLACK_ORB)
3759 Feld[x][y] = Store2[x][y];
3764 else if (phase == half_phase)
3766 int element = Store2[x][y];
3768 if (IS_PLAYER(x, y))
3769 KillHeroUnlessExplosionProtected(x, y);
3770 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3772 Feld[x][y] = Store2[x][y];
3776 else if (element == EL_AMOEBA_TO_DIAMOND)
3777 AmoebeUmwandeln(x, y);
3781 if (phase == last_phase)
3786 printf("::: done: phase == %d\n", phase);
3790 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3793 element = Feld[x][y] = Store[x][y];
3794 Store[x][y] = Store2[x][y] = 0;
3795 GfxElement[x][y] = EL_UNDEFINED;
3797 /* player can escape from explosions and might therefore be still alive */
3798 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3799 element <= EL_PLAYER_IS_EXPLODING_4)
3800 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3802 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3803 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3804 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3807 /* restore probably existing indestructible background element */
3808 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3809 element = Feld[x][y] = Back[x][y];
3812 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3813 GfxDir[x][y] = MV_NO_MOVING;
3814 ChangeDelay[x][y] = 0;
3815 ChangePage[x][y] = -1;
3818 InitField_WithBug2(x, y, FALSE);
3820 InitField(x, y, FALSE);
3822 /* !!! not needed !!! */
3824 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3825 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3828 if (CAN_MOVE(element))
3833 DrawLevelField(x, y);
3835 TestIfElementTouchesCustomElement(x, y);
3837 if (GFX_CRUMBLED(element))
3838 DrawLevelFieldCrumbledSandNeighbours(x, y);
3840 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3841 StorePlayer[x][y] = 0;
3843 if (ELEM_IS_PLAYER(element))
3844 RelocatePlayer(x, y, element);
3847 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3849 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3853 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3855 int stored = Store[x][y];
3856 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3857 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3861 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3863 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3867 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3871 printf("::: %d / %d [%d - %d]\n",
3872 GfxFrame[x][y], phase - delay, phase, delay);
3876 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3877 element_info[GfxElement[x][y]].token_name,
3882 DrawLevelFieldCrumbledSand(x, y);
3884 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3886 DrawLevelElement(x, y, Back[x][y]);
3887 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3889 else if (IS_WALKABLE_UNDER(Back[x][y]))
3891 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3892 DrawLevelElementThruMask(x, y, Back[x][y]);
3894 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3895 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3899 void DynaExplode(int ex, int ey)
3902 int dynabomb_element = Feld[ex][ey];
3903 int dynabomb_size = 1;
3904 boolean dynabomb_xl = FALSE;
3905 struct PlayerInfo *player;
3906 static int xy[4][2] =
3914 if (IS_ACTIVE_BOMB(dynabomb_element))
3916 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3917 dynabomb_size = player->dynabomb_size;
3918 dynabomb_xl = player->dynabomb_xl;
3919 player->dynabombs_left++;
3922 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3924 for (i = 0; i < NUM_DIRECTIONS; i++)
3926 for (j = 1; j <= dynabomb_size; j++)
3928 int x = ex + j * xy[i][0];
3929 int y = ey + j * xy[i][1];
3932 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3935 element = Feld[x][y];
3937 /* do not restart explosions of fields with active bombs */
3938 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3941 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3945 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3946 !IS_DIGGABLE(element) && !dynabomb_xl)
3949 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3950 !CAN_GROW_INTO(element) && !dynabomb_xl)
3954 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3955 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3956 element != EL_SAND && !dynabomb_xl)
3963 void Bang(int x, int y)
3966 int element = MovingOrBlocked2Element(x, y);
3968 int element = Feld[x][y];
3972 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3974 if (IS_PLAYER(x, y))
3977 struct PlayerInfo *player = PLAYERINFO(x, y);
3979 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3980 player->element_nr);
3985 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3987 if (game.emulation == EMU_SUPAPLEX)
3988 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3990 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3995 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
4003 case EL_BD_BUTTERFLY:
4006 case EL_DARK_YAMYAM:
4010 RaiseScoreElement(element);
4011 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4013 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4014 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4015 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4016 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4017 case EL_DYNABOMB_INCREASE_NUMBER:
4018 case EL_DYNABOMB_INCREASE_SIZE:
4019 case EL_DYNABOMB_INCREASE_POWER:
4024 case EL_LAMP_ACTIVE:
4026 case EL_AMOEBA_TO_DIAMOND:
4028 if (IS_PLAYER(x, y))
4029 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4031 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4035 if (element_info[element].explosion_type == EXPLODES_CROSS)
4037 if (CAN_EXPLODE_CROSS(element))
4040 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4045 else if (element_info[element].explosion_type == EXPLODES_1X1)
4047 else if (CAN_EXPLODE_1X1(element))
4049 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4051 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4055 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4058 void SplashAcid(int x, int y)
4061 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4062 (!IN_LEV_FIELD(x - 1, y - 2) ||
4063 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4064 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4066 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4067 (!IN_LEV_FIELD(x + 1, y - 2) ||
4068 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4069 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4071 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4073 /* input: position of element entering acid (obsolete) */
4075 int element = Feld[x][y];
4077 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4080 if (element != EL_ACID_SPLASH_LEFT &&
4081 element != EL_ACID_SPLASH_RIGHT)
4083 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4085 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4086 (!IN_LEV_FIELD(x - 1, y - 1) ||
4087 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4088 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4090 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4091 (!IN_LEV_FIELD(x + 1, y - 1) ||
4092 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4093 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4098 static void InitBeltMovement()
4100 static int belt_base_element[4] =
4102 EL_CONVEYOR_BELT_1_LEFT,
4103 EL_CONVEYOR_BELT_2_LEFT,
4104 EL_CONVEYOR_BELT_3_LEFT,
4105 EL_CONVEYOR_BELT_4_LEFT
4107 static int belt_base_active_element[4] =
4109 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4110 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4111 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4112 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4117 /* set frame order for belt animation graphic according to belt direction */
4118 for (i = 0; i < NUM_BELTS; i++)
4122 for (j = 0; j < NUM_BELT_PARTS; j++)
4124 int element = belt_base_active_element[belt_nr] + j;
4125 int graphic = el2img(element);
4127 if (game.belt_dir[i] == MV_LEFT)
4128 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4130 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4134 for (y = 0; y < lev_fieldy; y++)
4136 for (x = 0; x < lev_fieldx; x++)
4138 int element = Feld[x][y];
4140 for (i = 0; i < NUM_BELTS; i++)
4142 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4144 int e_belt_nr = getBeltNrFromBeltElement(element);
4147 if (e_belt_nr == belt_nr)
4149 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4151 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4159 static void ToggleBeltSwitch(int x, int y)
4161 static int belt_base_element[4] =
4163 EL_CONVEYOR_BELT_1_LEFT,
4164 EL_CONVEYOR_BELT_2_LEFT,
4165 EL_CONVEYOR_BELT_3_LEFT,
4166 EL_CONVEYOR_BELT_4_LEFT
4168 static int belt_base_active_element[4] =
4170 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4171 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4172 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4173 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4175 static int belt_base_switch_element[4] =
4177 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4178 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4179 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4180 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4182 static int belt_move_dir[4] =
4190 int element = Feld[x][y];
4191 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4192 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4193 int belt_dir = belt_move_dir[belt_dir_nr];
4196 if (!IS_BELT_SWITCH(element))
4199 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4200 game.belt_dir[belt_nr] = belt_dir;
4202 if (belt_dir_nr == 3)
4205 /* set frame order for belt animation graphic according to belt direction */
4206 for (i = 0; i < NUM_BELT_PARTS; i++)
4208 int element = belt_base_active_element[belt_nr] + i;
4209 int graphic = el2img(element);
4211 if (belt_dir == MV_LEFT)
4212 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4214 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4217 for (yy = 0; yy < lev_fieldy; yy++)
4219 for (xx = 0; xx < lev_fieldx; xx++)
4221 int element = Feld[xx][yy];
4223 if (IS_BELT_SWITCH(element))
4225 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4227 if (e_belt_nr == belt_nr)
4229 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4230 DrawLevelField(xx, yy);
4233 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4235 int e_belt_nr = getBeltNrFromBeltElement(element);
4237 if (e_belt_nr == belt_nr)
4239 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4241 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4242 DrawLevelField(xx, yy);
4245 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4247 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4249 if (e_belt_nr == belt_nr)
4251 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4253 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4254 DrawLevelField(xx, yy);
4261 static void ToggleSwitchgateSwitch(int x, int y)
4265 game.switchgate_pos = !game.switchgate_pos;
4267 for (yy = 0; yy < lev_fieldy; yy++)
4269 for (xx = 0; xx < lev_fieldx; xx++)
4271 int element = Feld[xx][yy];
4273 if (element == EL_SWITCHGATE_SWITCH_UP ||
4274 element == EL_SWITCHGATE_SWITCH_DOWN)
4276 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4277 DrawLevelField(xx, yy);
4279 else if (element == EL_SWITCHGATE_OPEN ||
4280 element == EL_SWITCHGATE_OPENING)
4282 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4284 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4286 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4289 else if (element == EL_SWITCHGATE_CLOSED ||
4290 element == EL_SWITCHGATE_CLOSING)
4292 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4294 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4296 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4303 static int getInvisibleActiveFromInvisibleElement(int element)
4305 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4306 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4307 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4311 static int getInvisibleFromInvisibleActiveElement(int element)
4313 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4314 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4315 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4319 static void RedrawAllLightSwitchesAndInvisibleElements()
4323 for (y = 0; y < lev_fieldy; y++)
4325 for (x = 0; x < lev_fieldx; x++)
4327 int element = Feld[x][y];
4329 if (element == EL_LIGHT_SWITCH &&
4330 game.light_time_left > 0)
4332 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4333 DrawLevelField(x, y);
4335 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4336 game.light_time_left == 0)
4338 Feld[x][y] = EL_LIGHT_SWITCH;
4339 DrawLevelField(x, y);
4341 else if (element == EL_INVISIBLE_STEELWALL ||
4342 element == EL_INVISIBLE_WALL ||
4343 element == EL_INVISIBLE_SAND)
4345 if (game.light_time_left > 0)
4346 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4348 DrawLevelField(x, y);
4350 /* uncrumble neighbour fields, if needed */
4351 if (element == EL_INVISIBLE_SAND)
4352 DrawLevelFieldCrumbledSandNeighbours(x, y);
4354 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4355 element == EL_INVISIBLE_WALL_ACTIVE ||
4356 element == EL_INVISIBLE_SAND_ACTIVE)
4358 if (game.light_time_left == 0)
4359 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4361 DrawLevelField(x, y);
4363 /* re-crumble neighbour fields, if needed */
4364 if (element == EL_INVISIBLE_SAND)
4365 DrawLevelFieldCrumbledSandNeighbours(x, y);
4371 static void ToggleLightSwitch(int x, int y)
4373 int element = Feld[x][y];
4375 game.light_time_left =
4376 (element == EL_LIGHT_SWITCH ?
4377 level.time_light * FRAMES_PER_SECOND : 0);
4379 RedrawAllLightSwitchesAndInvisibleElements();
4382 static void ActivateTimegateSwitch(int x, int y)
4386 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4388 for (yy = 0; yy < lev_fieldy; yy++)
4390 for (xx = 0; xx < lev_fieldx; xx++)
4392 int element = Feld[xx][yy];
4394 if (element == EL_TIMEGATE_CLOSED ||
4395 element == EL_TIMEGATE_CLOSING)
4397 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4398 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4402 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4404 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4405 DrawLevelField(xx, yy);
4412 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4415 void Impact(int x, int y)
4417 boolean last_line = (y == lev_fieldy - 1);
4418 boolean object_hit = FALSE;
4419 boolean impact = (last_line || object_hit);
4420 int element = Feld[x][y];
4421 int smashed = EL_STEELWALL;
4424 printf("IMPACT!\n");
4427 if (!last_line) /* check if element below was hit */
4429 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4432 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4433 MovDir[x][y + 1] != MV_DOWN ||
4434 MovPos[x][y + 1] <= TILEY / 2));
4437 object_hit = !IS_FREE(x, y + 1);
4440 /* do not smash moving elements that left the smashed field in time */
4441 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4442 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4446 smashed = MovingOrBlocked2Element(x, y + 1);
4448 impact = (last_line || object_hit);
4451 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4453 SplashAcid(x, y + 1);
4457 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4458 /* only reset graphic animation if graphic really changes after impact */
4460 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4462 ResetGfxAnimation(x, y);
4463 DrawLevelField(x, y);
4466 if (impact && CAN_EXPLODE_IMPACT(element))
4471 else if (impact && element == EL_PEARL)
4473 ResetGfxAnimation(x, y);
4475 Feld[x][y] = EL_PEARL_BREAKING;
4476 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4479 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4481 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4486 if (impact && element == EL_AMOEBA_DROP)
4488 if (object_hit && IS_PLAYER(x, y + 1))
4489 KillHeroUnlessEnemyProtected(x, y + 1);
4490 else if (object_hit && smashed == EL_PENGUIN)
4494 Feld[x][y] = EL_AMOEBA_GROWING;
4495 Store[x][y] = EL_AMOEBA_WET;
4497 ResetRandomAnimationValue(x, y);
4502 if (object_hit) /* check which object was hit */
4504 if (CAN_PASS_MAGIC_WALL(element) &&
4505 (smashed == EL_MAGIC_WALL ||
4506 smashed == EL_BD_MAGIC_WALL))
4509 int activated_magic_wall =
4510 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4511 EL_BD_MAGIC_WALL_ACTIVE);
4513 /* activate magic wall / mill */
4514 for (yy = 0; yy < lev_fieldy; yy++)
4515 for (xx = 0; xx < lev_fieldx; xx++)
4516 if (Feld[xx][yy] == smashed)
4517 Feld[xx][yy] = activated_magic_wall;
4519 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4520 game.magic_wall_active = TRUE;
4522 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4523 SND_MAGIC_WALL_ACTIVATING :
4524 SND_BD_MAGIC_WALL_ACTIVATING));
4527 if (IS_PLAYER(x, y + 1))
4529 if (CAN_SMASH_PLAYER(element))
4531 KillHeroUnlessEnemyProtected(x, y + 1);
4535 else if (smashed == EL_PENGUIN)
4537 if (CAN_SMASH_PLAYER(element))
4543 else if (element == EL_BD_DIAMOND)
4545 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4551 else if (((element == EL_SP_INFOTRON ||
4552 element == EL_SP_ZONK) &&
4553 (smashed == EL_SP_SNIKSNAK ||
4554 smashed == EL_SP_ELECTRON ||
4555 smashed == EL_SP_DISK_ORANGE)) ||
4556 (element == EL_SP_INFOTRON &&
4557 smashed == EL_SP_DISK_YELLOW))
4563 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4569 else if (CAN_SMASH_EVERYTHING(element))
4571 if (IS_CLASSIC_ENEMY(smashed) ||
4572 CAN_EXPLODE_SMASHED(smashed))
4577 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4579 if (smashed == EL_LAMP ||
4580 smashed == EL_LAMP_ACTIVE)
4585 else if (smashed == EL_NUT)
4587 Feld[x][y + 1] = EL_NUT_BREAKING;
4588 PlayLevelSound(x, y, SND_NUT_BREAKING);
4589 RaiseScoreElement(EL_NUT);
4592 else if (smashed == EL_PEARL)
4594 ResetGfxAnimation(x, y);
4596 Feld[x][y + 1] = EL_PEARL_BREAKING;
4597 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4600 else if (smashed == EL_DIAMOND)
4602 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4603 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4606 else if (IS_BELT_SWITCH(smashed))
4608 ToggleBeltSwitch(x, y + 1);
4610 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4611 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4613 ToggleSwitchgateSwitch(x, y + 1);
4615 else if (smashed == EL_LIGHT_SWITCH ||
4616 smashed == EL_LIGHT_SWITCH_ACTIVE)
4618 ToggleLightSwitch(x, y + 1);
4623 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4626 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4629 /* !!! TEST ONLY !!! */
4630 CheckElementChangeBySide(x, y + 1, smashed, element,
4631 CE_SWITCHED, CH_SIDE_TOP);
4632 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4633 CE_SWITCH_OF_X, CH_SIDE_TOP);
4635 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4636 CE_SWITCH_OF_X, CH_SIDE_TOP);
4637 CheckElementChangeBySide(x, y + 1, smashed, element,
4638 CE_SWITCHED, CH_SIDE_TOP);
4644 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4649 /* play sound of magic wall / mill */
4651 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4652 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4654 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4655 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4656 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4657 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4662 /* play sound of object that hits the ground */
4663 if (last_line || object_hit)
4664 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4667 inline static void TurnRoundExt(int x, int y)
4679 { 0, 0 }, { 0, 0 }, { 0, 0 },
4684 int left, right, back;
4688 { MV_DOWN, MV_UP, MV_RIGHT },
4689 { MV_UP, MV_DOWN, MV_LEFT },
4691 { MV_LEFT, MV_RIGHT, MV_DOWN },
4695 { MV_RIGHT, MV_LEFT, MV_UP }
4698 int element = Feld[x][y];
4699 int move_pattern = element_info[element].move_pattern;
4701 int old_move_dir = MovDir[x][y];
4702 int left_dir = turn[old_move_dir].left;
4703 int right_dir = turn[old_move_dir].right;
4704 int back_dir = turn[old_move_dir].back;
4706 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4707 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4708 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4709 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4711 int left_x = x + left_dx, left_y = y + left_dy;
4712 int right_x = x + right_dx, right_y = y + right_dy;
4713 int move_x = x + move_dx, move_y = y + move_dy;
4717 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4719 TestIfBadThingTouchesOtherBadThing(x, y);
4721 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4722 MovDir[x][y] = right_dir;
4723 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4724 MovDir[x][y] = left_dir;
4726 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4728 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4732 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4733 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4735 TestIfBadThingTouchesOtherBadThing(x, y);
4737 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4738 MovDir[x][y] = left_dir;
4739 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4740 MovDir[x][y] = right_dir;
4742 if ((element == EL_SPACESHIP ||
4743 element == EL_SP_SNIKSNAK ||
4744 element == EL_SP_ELECTRON)
4745 && MovDir[x][y] != old_move_dir)
4747 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4751 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4753 TestIfBadThingTouchesOtherBadThing(x, y);
4755 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4756 MovDir[x][y] = left_dir;
4757 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4758 MovDir[x][y] = right_dir;
4760 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4762 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4765 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4767 TestIfBadThingTouchesOtherBadThing(x, y);
4769 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4770 MovDir[x][y] = left_dir;
4771 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4772 MovDir[x][y] = right_dir;
4774 if (MovDir[x][y] != old_move_dir)
4778 else if (element == EL_YAMYAM)
4780 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4781 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4783 if (can_turn_left && can_turn_right)
4784 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4785 else if (can_turn_left)
4786 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4787 else if (can_turn_right)
4788 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4790 MovDir[x][y] = back_dir;
4792 MovDelay[x][y] = 16 + 16 * RND(3);
4794 else if (element == EL_DARK_YAMYAM)
4796 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4798 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4801 if (can_turn_left && can_turn_right)
4802 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4803 else if (can_turn_left)
4804 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4805 else if (can_turn_right)
4806 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4808 MovDir[x][y] = back_dir;
4810 MovDelay[x][y] = 16 + 16 * RND(3);
4812 else if (element == EL_PACMAN)
4814 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4815 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4817 if (can_turn_left && can_turn_right)
4818 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4819 else if (can_turn_left)
4820 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4821 else if (can_turn_right)
4822 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4824 MovDir[x][y] = back_dir;
4826 MovDelay[x][y] = 6 + RND(40);
4828 else if (element == EL_PIG)
4830 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4831 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4832 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4833 boolean should_turn_left, should_turn_right, should_move_on;
4835 int rnd = RND(rnd_value);
4837 should_turn_left = (can_turn_left &&
4839 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4840 y + back_dy + left_dy)));
4841 should_turn_right = (can_turn_right &&
4843 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4844 y + back_dy + right_dy)));
4845 should_move_on = (can_move_on &&
4848 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4849 y + move_dy + left_dy) ||
4850 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4851 y + move_dy + right_dy)));
4853 if (should_turn_left || should_turn_right || should_move_on)
4855 if (should_turn_left && should_turn_right && should_move_on)
4856 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4857 rnd < 2 * rnd_value / 3 ? right_dir :
4859 else if (should_turn_left && should_turn_right)
4860 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4861 else if (should_turn_left && should_move_on)
4862 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4863 else if (should_turn_right && should_move_on)
4864 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4865 else if (should_turn_left)
4866 MovDir[x][y] = left_dir;
4867 else if (should_turn_right)
4868 MovDir[x][y] = right_dir;
4869 else if (should_move_on)
4870 MovDir[x][y] = old_move_dir;
4872 else if (can_move_on && rnd > rnd_value / 8)
4873 MovDir[x][y] = old_move_dir;
4874 else if (can_turn_left && can_turn_right)
4875 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4876 else if (can_turn_left && rnd > rnd_value / 8)
4877 MovDir[x][y] = left_dir;
4878 else if (can_turn_right && rnd > rnd_value/8)
4879 MovDir[x][y] = right_dir;
4881 MovDir[x][y] = back_dir;
4883 xx = x + move_xy[MovDir[x][y]].x;
4884 yy = y + move_xy[MovDir[x][y]].y;
4887 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4888 if (!IN_LEV_FIELD(xx, yy) ||
4889 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4890 MovDir[x][y] = old_move_dir;
4892 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4893 MovDir[x][y] = old_move_dir;
4898 else if (element == EL_DRAGON)
4900 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4901 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4902 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4904 int rnd = RND(rnd_value);
4907 if (FrameCounter < 1 && x == 0 && y == 29)
4908 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4911 if (can_move_on && rnd > rnd_value / 8)
4912 MovDir[x][y] = old_move_dir;
4913 else if (can_turn_left && can_turn_right)
4914 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4915 else if (can_turn_left && rnd > rnd_value / 8)
4916 MovDir[x][y] = left_dir;
4917 else if (can_turn_right && rnd > rnd_value / 8)
4918 MovDir[x][y] = right_dir;
4920 MovDir[x][y] = back_dir;
4922 xx = x + move_xy[MovDir[x][y]].x;
4923 yy = y + move_xy[MovDir[x][y]].y;
4926 if (FrameCounter < 1 && x == 0 && y == 29)
4927 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4928 xx, yy, Feld[xx][yy],
4933 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4934 MovDir[x][y] = old_move_dir;
4936 if (!IS_FREE(xx, yy))
4937 MovDir[x][y] = old_move_dir;
4941 if (FrameCounter < 1 && x == 0 && y == 29)
4942 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4947 else if (element == EL_MOLE)
4949 boolean can_move_on =
4950 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4951 IS_AMOEBOID(Feld[move_x][move_y]) ||
4952 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4955 boolean can_turn_left =
4956 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4957 IS_AMOEBOID(Feld[left_x][left_y])));
4959 boolean can_turn_right =
4960 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4961 IS_AMOEBOID(Feld[right_x][right_y])));
4963 if (can_turn_left && can_turn_right)
4964 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4965 else if (can_turn_left)
4966 MovDir[x][y] = left_dir;
4968 MovDir[x][y] = right_dir;
4971 if (MovDir[x][y] != old_move_dir)
4974 else if (element == EL_BALLOON)
4976 MovDir[x][y] = game.balloon_dir;
4979 else if (element == EL_SPRING)
4982 if (MovDir[x][y] & MV_HORIZONTAL &&
4983 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4984 MovDir[x][y] = MV_NO_MOVING;
4986 if (MovDir[x][y] & MV_HORIZONTAL &&
4987 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4988 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4989 MovDir[x][y] = MV_NO_MOVING;
4994 else if (element == EL_ROBOT ||
4995 element == EL_SATELLITE ||
4996 element == EL_PENGUIN)
4998 int attr_x = -1, attr_y = -1;
5009 for (i = 0; i < MAX_PLAYERS; i++)
5011 struct PlayerInfo *player = &stored_player[i];
5012 int jx = player->jx, jy = player->jy;
5014 if (!player->active)
5018 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5027 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5028 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5029 game.engine_version < VERSION_IDENT(3,1,0,0)))
5031 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5038 if (element == EL_PENGUIN)
5041 static int xy[4][2] =
5049 for (i = 0; i < NUM_DIRECTIONS; i++)
5051 int ex = x + xy[i][0];
5052 int ey = y + xy[i][1];
5054 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5063 MovDir[x][y] = MV_NO_MOVING;
5065 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5066 else if (attr_x > x)
5067 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5069 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5070 else if (attr_y > y)
5071 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5073 if (element == EL_ROBOT)
5077 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5078 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5079 Moving2Blocked(x, y, &newx, &newy);
5081 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5082 MovDelay[x][y] = 8 + 8 * !RND(3);
5084 MovDelay[x][y] = 16;
5086 else if (element == EL_PENGUIN)
5092 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5094 boolean first_horiz = RND(2);
5095 int new_move_dir = MovDir[x][y];
5098 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5099 Moving2Blocked(x, y, &newx, &newy);
5101 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5105 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5106 Moving2Blocked(x, y, &newx, &newy);
5108 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5111 MovDir[x][y] = old_move_dir;
5115 else /* (element == EL_SATELLITE) */
5121 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5123 boolean first_horiz = RND(2);
5124 int new_move_dir = MovDir[x][y];
5127 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5128 Moving2Blocked(x, y, &newx, &newy);
5130 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5134 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5135 Moving2Blocked(x, y, &newx, &newy);
5137 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5140 MovDir[x][y] = old_move_dir;
5145 else if (move_pattern == MV_TURNING_LEFT ||
5146 move_pattern == MV_TURNING_RIGHT ||
5147 move_pattern == MV_TURNING_LEFT_RIGHT ||
5148 move_pattern == MV_TURNING_RIGHT_LEFT ||
5149 move_pattern == MV_TURNING_RANDOM ||
5150 move_pattern == MV_ALL_DIRECTIONS)
5152 boolean can_turn_left =
5153 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5154 boolean can_turn_right =
5155 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5157 #if USE_CAN_MOVE_NOT_MOVING
5158 if (element_info[element].move_stepsize == 0) /* not moving */
5162 if (move_pattern == MV_TURNING_LEFT)
5163 MovDir[x][y] = left_dir;
5164 else if (move_pattern == MV_TURNING_RIGHT)
5165 MovDir[x][y] = right_dir;
5166 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5167 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5168 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5169 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5170 else if (move_pattern == MV_TURNING_RANDOM)
5171 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5172 can_turn_right && !can_turn_left ? right_dir :
5173 RND(2) ? left_dir : right_dir);
5174 else if (can_turn_left && can_turn_right)
5175 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5176 else if (can_turn_left)
5177 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5178 else if (can_turn_right)
5179 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5181 MovDir[x][y] = back_dir;
5183 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5185 else if (move_pattern == MV_HORIZONTAL ||
5186 move_pattern == MV_VERTICAL)
5188 if (move_pattern & old_move_dir)
5189 MovDir[x][y] = back_dir;
5190 else if (move_pattern == MV_HORIZONTAL)
5191 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5192 else if (move_pattern == MV_VERTICAL)
5193 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5195 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5197 else if (move_pattern & MV_ANY_DIRECTION)
5199 MovDir[x][y] = move_pattern;
5200 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5202 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5204 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5205 MovDir[x][y] = left_dir;
5206 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5207 MovDir[x][y] = right_dir;
5209 if (MovDir[x][y] != old_move_dir)
5210 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5212 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5214 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5215 MovDir[x][y] = right_dir;
5216 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5217 MovDir[x][y] = left_dir;
5219 if (MovDir[x][y] != old_move_dir)
5220 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5222 else if (move_pattern == MV_TOWARDS_PLAYER ||
5223 move_pattern == MV_AWAY_FROM_PLAYER)
5225 int attr_x = -1, attr_y = -1;
5227 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5238 for (i = 0; i < MAX_PLAYERS; i++)
5240 struct PlayerInfo *player = &stored_player[i];
5241 int jx = player->jx, jy = player->jy;
5243 if (!player->active)
5247 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5255 MovDir[x][y] = MV_NO_MOVING;
5257 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5258 else if (attr_x > x)
5259 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5261 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5262 else if (attr_y > y)
5263 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5265 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5267 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5269 boolean first_horiz = RND(2);
5270 int new_move_dir = MovDir[x][y];
5272 #if USE_CAN_MOVE_NOT_MOVING
5273 if (element_info[element].move_stepsize == 0) /* not moving */
5275 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5276 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5283 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5284 Moving2Blocked(x, y, &newx, &newy);
5286 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5290 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5291 Moving2Blocked(x, y, &newx, &newy);
5293 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5296 MovDir[x][y] = old_move_dir;
5299 else if (move_pattern == MV_WHEN_PUSHED ||
5300 move_pattern == MV_WHEN_DROPPED)
5302 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5303 MovDir[x][y] = MV_NO_MOVING;
5307 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5309 static int test_xy[7][2] =
5319 static int test_dir[7] =
5329 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5330 int move_preference = -1000000; /* start with very low preference */
5331 int new_move_dir = MV_NO_MOVING;
5332 int start_test = RND(4);
5335 for (i = 0; i < NUM_DIRECTIONS; i++)
5337 int move_dir = test_dir[start_test + i];
5338 int move_dir_preference;
5340 xx = x + test_xy[start_test + i][0];
5341 yy = y + test_xy[start_test + i][1];
5343 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5344 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5346 new_move_dir = move_dir;
5351 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5354 move_dir_preference = -1 * RunnerVisit[xx][yy];
5355 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5356 move_dir_preference = PlayerVisit[xx][yy];
5358 if (move_dir_preference > move_preference)
5360 /* prefer field that has not been visited for the longest time */
5361 move_preference = move_dir_preference;
5362 new_move_dir = move_dir;
5364 else if (move_dir_preference == move_preference &&
5365 move_dir == old_move_dir)
5367 /* prefer last direction when all directions are preferred equally */
5368 move_preference = move_dir_preference;
5369 new_move_dir = move_dir;
5373 MovDir[x][y] = new_move_dir;
5374 if (old_move_dir != new_move_dir)
5377 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5385 static void TurnRound(int x, int y)
5387 int direction = MovDir[x][y];
5390 GfxDir[x][y] = MovDir[x][y];
5396 GfxDir[x][y] = MovDir[x][y];
5399 if (direction != MovDir[x][y])
5404 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5407 GfxAction[x][y] = ACTION_WAITING;
5411 static boolean JustBeingPushed(int x, int y)
5415 for (i = 0; i < MAX_PLAYERS; i++)
5417 struct PlayerInfo *player = &stored_player[i];
5419 if (player->active && player->is_pushing && player->MovPos)
5421 int next_jx = player->jx + (player->jx - player->last_jx);
5422 int next_jy = player->jy + (player->jy - player->last_jy);
5424 if (x == next_jx && y == next_jy)
5432 void StartMoving(int x, int y)
5435 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5437 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5438 int element = Feld[x][y];
5444 if (MovDelay[x][y] == 0)
5445 GfxAction[x][y] = ACTION_DEFAULT;
5447 /* !!! this should be handled more generic (not only for mole) !!! */
5448 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5449 GfxAction[x][y] = ACTION_DEFAULT;
5452 if (CAN_FALL(element) && y < lev_fieldy - 1)
5454 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5455 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5456 if (JustBeingPushed(x, y))
5459 if (element == EL_QUICKSAND_FULL)
5461 if (IS_FREE(x, y + 1))
5463 InitMovingField(x, y, MV_DOWN);
5464 started_moving = TRUE;
5466 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5467 Store[x][y] = EL_ROCK;
5469 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5471 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5474 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5476 if (!MovDelay[x][y])
5477 MovDelay[x][y] = TILEY + 1;
5486 Feld[x][y] = EL_QUICKSAND_EMPTY;
5487 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5488 Store[x][y + 1] = Store[x][y];
5491 PlayLevelSoundAction(x, y, ACTION_FILLING);
5493 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5497 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5498 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5500 InitMovingField(x, y, MV_DOWN);
5501 started_moving = TRUE;
5503 Feld[x][y] = EL_QUICKSAND_FILLING;
5504 Store[x][y] = element;
5506 PlayLevelSoundAction(x, y, ACTION_FILLING);
5508 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5511 else if (element == EL_MAGIC_WALL_FULL)
5513 if (IS_FREE(x, y + 1))
5515 InitMovingField(x, y, MV_DOWN);
5516 started_moving = TRUE;
5518 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5519 Store[x][y] = EL_CHANGED(Store[x][y]);
5521 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5523 if (!MovDelay[x][y])
5524 MovDelay[x][y] = TILEY/4 + 1;
5533 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5534 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5535 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5539 else if (element == EL_BD_MAGIC_WALL_FULL)
5541 if (IS_FREE(x, y + 1))
5543 InitMovingField(x, y, MV_DOWN);
5544 started_moving = TRUE;
5546 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5547 Store[x][y] = EL_CHANGED2(Store[x][y]);
5549 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5551 if (!MovDelay[x][y])
5552 MovDelay[x][y] = TILEY/4 + 1;
5561 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5562 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5563 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5567 else if (CAN_PASS_MAGIC_WALL(element) &&
5568 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5569 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5571 InitMovingField(x, y, MV_DOWN);
5572 started_moving = TRUE;
5575 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5576 EL_BD_MAGIC_WALL_FILLING);
5577 Store[x][y] = element;
5580 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5582 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5585 SplashAcid(x, y + 1);
5587 InitMovingField(x, y, MV_DOWN);
5588 started_moving = TRUE;
5590 Store[x][y] = EL_ACID;
5592 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5593 GfxAction[x][y + 1] = ACTION_ACTIVE;
5597 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5598 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5600 #if USE_IMPACT_BUGFIX
5601 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5602 CAN_FALL(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_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5607 (Feld[x][y + 1] == EL_BLOCKED)))
5609 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5610 CAN_SMASH(element) && WasJustFalling[x][y] &&
5611 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5613 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5614 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5615 (Feld[x][y + 1] == EL_BLOCKED)))
5620 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5621 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5622 WasJustMoving[x][y] && !Pushed[x][y + 1])
5624 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5625 WasJustMoving[x][y])
5630 /* this is needed for a special case not covered by calling "Impact()"
5631 from "ContinueMoving()": if an element moves to a tile directly below
5632 another element which was just falling on that tile (which was empty
5633 in the previous frame), the falling element above would just stop
5634 instead of smashing the element below (in previous version, the above
5635 element was just checked for "moving" instead of "falling", resulting
5636 in incorrect smashes caused by horizontal movement of the above
5637 element; also, the case of the player being the element to smash was
5638 simply not covered here... :-/ ) */
5641 WasJustMoving[x][y] = 0;
5642 WasJustFalling[x][y] = 0;
5645 CheckCollision[x][y] = 0;
5648 if (IS_PLAYER(x, y + 1))
5649 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5654 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5656 if (MovDir[x][y] == MV_NO_MOVING)
5658 InitMovingField(x, y, MV_DOWN);
5659 started_moving = TRUE;
5662 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5664 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5665 MovDir[x][y] = MV_DOWN;
5667 InitMovingField(x, y, MV_DOWN);
5668 started_moving = TRUE;
5670 else if (element == EL_AMOEBA_DROP)
5672 Feld[x][y] = EL_AMOEBA_GROWING;
5673 Store[x][y] = EL_AMOEBA_WET;
5675 /* Store[x][y + 1] must be zero, because:
5676 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5679 #if OLD_GAME_BEHAVIOUR
5680 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5682 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5683 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5684 element != EL_DX_SUPABOMB)
5687 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5688 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5689 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5690 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5693 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5694 (IS_FREE(x - 1, y + 1) ||
5695 Feld[x - 1][y + 1] == EL_ACID));
5696 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5697 (IS_FREE(x + 1, y + 1) ||
5698 Feld[x + 1][y + 1] == EL_ACID));
5699 boolean can_fall_any = (can_fall_left || can_fall_right);
5700 boolean can_fall_both = (can_fall_left && can_fall_right);
5702 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5704 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5706 if (slippery_type == SLIPPERY_ONLY_LEFT)
5707 can_fall_right = FALSE;
5708 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5709 can_fall_left = FALSE;
5710 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5711 can_fall_right = FALSE;
5712 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5713 can_fall_left = FALSE;
5715 can_fall_any = (can_fall_left || can_fall_right);
5716 can_fall_both = (can_fall_left && can_fall_right);
5719 #if USE_NEW_SP_SLIPPERY
5720 /* !!! better use the same properties as for custom elements here !!! */
5721 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5722 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5724 can_fall_right = FALSE; /* slip down on left side */
5725 can_fall_both = FALSE;
5732 if (game.emulation == EMU_BOULDERDASH ||
5733 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5734 can_fall_right = FALSE; /* slip down on left side */
5736 can_fall_left = !(can_fall_right = RND(2));
5738 can_fall_both = FALSE;
5745 if (can_fall_both &&
5746 (game.emulation != EMU_BOULDERDASH &&
5747 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5748 can_fall_left = !(can_fall_right = RND(2));
5751 /* if not determined otherwise, prefer left side for slipping down */
5752 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5753 started_moving = TRUE;
5757 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5759 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5762 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5763 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5764 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5765 int belt_dir = game.belt_dir[belt_nr];
5767 if ((belt_dir == MV_LEFT && left_is_free) ||
5768 (belt_dir == MV_RIGHT && right_is_free))
5771 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5774 InitMovingField(x, y, belt_dir);
5775 started_moving = TRUE;
5778 Pushed[x][y] = TRUE;
5779 Pushed[nextx][y] = TRUE;
5782 GfxAction[x][y] = ACTION_DEFAULT;
5786 MovDir[x][y] = 0; /* if element was moving, stop it */
5791 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5793 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5795 if (CAN_MOVE(element) && !started_moving)
5798 int move_pattern = element_info[element].move_pattern;
5803 if (MovDir[x][y] == MV_NO_MOVING)
5805 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5806 x, y, element, element_info[element].token_name);
5807 printf("StartMoving(): This should never happen!\n");
5812 Moving2Blocked(x, y, &newx, &newy);
5815 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5818 if ((element == EL_SATELLITE ||
5819 element == EL_BALLOON ||
5820 element == EL_SPRING)
5821 && JustBeingPushed(x, y))
5828 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5829 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5831 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5832 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5833 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5837 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5838 element, element_info[element].token_name,
5839 WasJustMoving[x][y],
5840 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5841 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5842 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5843 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5847 WasJustMoving[x][y] = 0;
5850 CheckCollision[x][y] = 0;
5852 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5855 if (Feld[x][y] != element) /* element has changed */
5857 element = Feld[x][y];
5858 move_pattern = element_info[element].move_pattern;
5860 if (!CAN_MOVE(element))
5864 if (Feld[x][y] != element) /* element has changed */
5872 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5873 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5875 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5877 Moving2Blocked(x, y, &newx, &newy);
5878 if (Feld[newx][newy] == EL_BLOCKED)
5879 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5885 if (FrameCounter < 1 && x == 0 && y == 29)
5886 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5889 if (!MovDelay[x][y]) /* start new movement phase */
5891 /* all objects that can change their move direction after each step
5892 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5894 if (element != EL_YAMYAM &&
5895 element != EL_DARK_YAMYAM &&
5896 element != EL_PACMAN &&
5897 !(move_pattern & MV_ANY_DIRECTION) &&
5898 move_pattern != MV_TURNING_LEFT &&
5899 move_pattern != MV_TURNING_RIGHT &&
5900 move_pattern != MV_TURNING_LEFT_RIGHT &&
5901 move_pattern != MV_TURNING_RIGHT_LEFT &&
5902 move_pattern != MV_TURNING_RANDOM)
5907 if (FrameCounter < 1 && x == 0 && y == 29)
5908 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5911 if (MovDelay[x][y] && (element == EL_BUG ||
5912 element == EL_SPACESHIP ||
5913 element == EL_SP_SNIKSNAK ||
5914 element == EL_SP_ELECTRON ||
5915 element == EL_MOLE))
5916 DrawLevelField(x, y);
5920 if (MovDelay[x][y]) /* wait some time before next movement */
5925 if (element == EL_YAMYAM)
5928 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5929 DrawLevelElementAnimation(x, y, element);
5933 if (MovDelay[x][y]) /* element still has to wait some time */
5936 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5937 ResetGfxAnimation(x, y);
5941 if (GfxAction[x][y] != ACTION_WAITING)
5942 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5944 GfxAction[x][y] = ACTION_WAITING;
5948 if (element == EL_ROBOT ||
5950 element == EL_PACMAN ||
5952 element == EL_YAMYAM ||
5953 element == EL_DARK_YAMYAM)
5956 DrawLevelElementAnimation(x, y, element);
5958 DrawLevelElementAnimationIfNeeded(x, y, element);
5960 PlayLevelSoundAction(x, y, ACTION_WAITING);
5962 else if (element == EL_SP_ELECTRON)
5963 DrawLevelElementAnimationIfNeeded(x, y, element);
5964 else if (element == EL_DRAGON)
5967 int dir = MovDir[x][y];
5968 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5969 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5970 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5971 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5972 dir == MV_UP ? IMG_FLAMES_1_UP :
5973 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5974 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5977 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5980 GfxAction[x][y] = ACTION_ATTACKING;
5982 if (IS_PLAYER(x, y))
5983 DrawPlayerField(x, y);
5985 DrawLevelField(x, y);
5987 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5989 for (i = 1; i <= 3; i++)
5991 int xx = x + i * dx;
5992 int yy = y + i * dy;
5993 int sx = SCREENX(xx);
5994 int sy = SCREENY(yy);
5995 int flame_graphic = graphic + (i - 1);
5997 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6002 int flamed = MovingOrBlocked2Element(xx, yy);
6006 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6008 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6009 RemoveMovingField(xx, yy);
6011 RemoveField(xx, yy);
6013 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6016 RemoveMovingField(xx, yy);
6020 if (ChangeDelay[xx][yy])
6021 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6022 Feld[xx][yy] == EL_BLOCKED));
6026 ChangeDelay[xx][yy] = 0;
6028 Feld[xx][yy] = EL_FLAMES;
6029 if (IN_SCR_FIELD(sx, sy))
6031 DrawLevelFieldCrumbledSand(xx, yy);
6032 DrawGraphic(sx, sy, flame_graphic, frame);
6037 if (Feld[xx][yy] == EL_FLAMES)
6038 Feld[xx][yy] = EL_EMPTY;
6039 DrawLevelField(xx, yy);
6044 if (MovDelay[x][y]) /* element still has to wait some time */
6046 PlayLevelSoundAction(x, y, ACTION_WAITING);
6052 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6053 for all other elements GfxAction will be set by InitMovingField() */
6054 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6055 GfxAction[x][y] = ACTION_MOVING;
6059 /* now make next step */
6061 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6063 if (DONT_COLLIDE_WITH(element) &&
6064 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6065 !PLAYER_ENEMY_PROTECTED(newx, newy))
6068 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6072 /* player killed by element which is deadly when colliding with */
6074 KillHero(PLAYERINFO(newx, newy));
6081 else if (CAN_MOVE_INTO_ACID(element) &&
6082 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6083 (MovDir[x][y] == MV_DOWN ||
6084 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6086 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6087 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6091 else if ((element == EL_PENGUIN ||
6092 element == EL_ROBOT ||
6093 element == EL_SATELLITE ||
6094 element == EL_BALLOON ||
6095 IS_CUSTOM_ELEMENT(element)) &&
6096 IN_LEV_FIELD(newx, newy) &&
6097 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6100 SplashAcid(newx, newy);
6101 Store[x][y] = EL_ACID;
6103 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6105 if (Feld[newx][newy] == EL_EXIT_OPEN)
6109 DrawLevelField(x, y);
6111 Feld[x][y] = EL_EMPTY;
6112 DrawLevelField(x, y);
6115 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6116 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6117 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6119 local_player->friends_still_needed--;
6120 if (!local_player->friends_still_needed &&
6121 !local_player->GameOver && AllPlayersGone)
6122 local_player->LevelSolved = local_player->GameOver = TRUE;
6126 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6128 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6129 DrawLevelField(newx, newy);
6131 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6133 else if (!IS_FREE(newx, newy))
6135 GfxAction[x][y] = ACTION_WAITING;
6137 if (IS_PLAYER(x, y))
6138 DrawPlayerField(x, y);
6140 DrawLevelField(x, y);
6145 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6147 if (IS_FOOD_PIG(Feld[newx][newy]))
6149 if (IS_MOVING(newx, newy))
6150 RemoveMovingField(newx, newy);
6153 Feld[newx][newy] = EL_EMPTY;
6154 DrawLevelField(newx, newy);
6157 PlayLevelSound(x, y, SND_PIG_DIGGING);
6159 else if (!IS_FREE(newx, newy))
6161 if (IS_PLAYER(x, y))
6162 DrawPlayerField(x, y);
6164 DrawLevelField(x, y);
6173 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6176 else if (IS_CUSTOM_ELEMENT(element) &&
6177 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6181 !IS_FREE(newx, newy)
6186 int new_element = Feld[newx][newy];
6189 printf("::: '%s' digs '%s' [%d]\n",
6190 element_info[element].token_name,
6191 element_info[Feld[newx][newy]].token_name,
6192 StorePlayer[newx][newy]);
6195 if (!IS_FREE(newx, newy))
6197 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6198 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6201 /* no element can dig solid indestructible elements */
6202 if (IS_INDESTRUCTIBLE(new_element) &&
6203 !IS_DIGGABLE(new_element) &&
6204 !IS_COLLECTIBLE(new_element))
6207 if (AmoebaNr[newx][newy] &&
6208 (new_element == EL_AMOEBA_FULL ||
6209 new_element == EL_BD_AMOEBA ||
6210 new_element == EL_AMOEBA_GROWING))
6212 AmoebaCnt[AmoebaNr[newx][newy]]--;
6213 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6216 if (IS_MOVING(newx, newy))
6217 RemoveMovingField(newx, newy);
6220 RemoveField(newx, newy);
6221 DrawLevelField(newx, newy);
6224 /* if digged element was about to explode, prevent the explosion */
6225 ExplodeField[newx][newy] = EX_TYPE_NONE;
6227 PlayLevelSoundAction(x, y, action);
6232 Store[newx][newy] = EL_EMPTY;
6233 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6235 #if USE_CHANGE_TO_TRIGGERED
6236 int move_leave_element = element_info[element].move_leave_element;
6238 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6239 new_element : move_leave_element);
6241 Store[newx][newy] = element_info[element].move_leave_element;
6245 Store[newx][newy] = EL_EMPTY;
6246 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6247 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6248 Store[newx][newy] = element_info[element].move_leave_element;
6251 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6252 element_info[element].can_leave_element = TRUE;
6255 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6257 RunnerVisit[x][y] = FrameCounter;
6258 PlayerVisit[x][y] /= 8; /* expire player visit path */
6264 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6266 if (!IS_FREE(newx, newy))
6268 if (IS_PLAYER(x, y))
6269 DrawPlayerField(x, y);
6271 DrawLevelField(x, y);
6277 boolean wanna_flame = !RND(10);
6278 int dx = newx - x, dy = newy - y;
6279 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6280 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6281 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6282 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6283 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6284 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6287 IS_CLASSIC_ENEMY(element1) ||
6288 IS_CLASSIC_ENEMY(element2)) &&
6289 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6290 element1 != EL_FLAMES && element2 != EL_FLAMES)
6293 ResetGfxAnimation(x, y);
6294 GfxAction[x][y] = ACTION_ATTACKING;
6297 if (IS_PLAYER(x, y))
6298 DrawPlayerField(x, y);
6300 DrawLevelField(x, y);
6302 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6304 MovDelay[x][y] = 50;
6308 RemoveField(newx, newy);
6310 Feld[newx][newy] = EL_FLAMES;
6311 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6314 RemoveField(newx1, newy1);
6316 Feld[newx1][newy1] = EL_FLAMES;
6318 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6321 RemoveField(newx2, newy2);
6323 Feld[newx2][newy2] = EL_FLAMES;
6330 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6331 Feld[newx][newy] == EL_DIAMOND)
6333 if (IS_MOVING(newx, newy))
6334 RemoveMovingField(newx, newy);
6337 Feld[newx][newy] = EL_EMPTY;
6338 DrawLevelField(newx, newy);
6341 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6343 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6344 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6346 if (AmoebaNr[newx][newy])
6348 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6349 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6350 Feld[newx][newy] == EL_BD_AMOEBA)
6351 AmoebaCnt[AmoebaNr[newx][newy]]--;
6356 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6358 if (IS_MOVING(newx, newy))
6361 RemoveMovingField(newx, newy);
6365 Feld[newx][newy] = EL_EMPTY;
6366 DrawLevelField(newx, newy);
6369 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6371 else if ((element == EL_PACMAN || element == EL_MOLE)
6372 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6374 if (AmoebaNr[newx][newy])
6376 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6377 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6378 Feld[newx][newy] == EL_BD_AMOEBA)
6379 AmoebaCnt[AmoebaNr[newx][newy]]--;
6382 if (element == EL_MOLE)
6384 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6385 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6387 ResetGfxAnimation(x, y);
6388 GfxAction[x][y] = ACTION_DIGGING;
6389 DrawLevelField(x, y);
6391 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6393 return; /* wait for shrinking amoeba */
6395 else /* element == EL_PACMAN */
6397 Feld[newx][newy] = EL_EMPTY;
6398 DrawLevelField(newx, newy);
6399 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6402 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6403 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6404 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6406 /* wait for shrinking amoeba to completely disappear */
6409 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6411 /* object was running against a wall */
6416 if (move_pattern & MV_ANY_DIRECTION &&
6417 move_pattern == MovDir[x][y])
6419 int blocking_element =
6420 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6423 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6424 element_info[element].token_name,
6425 element_info[blocking_element].token_name,
6429 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6432 element = Feld[x][y]; /* element might have changed */
6437 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6438 DrawLevelElementAnimation(x, y, element);
6440 if (element == EL_BUG ||
6441 element == EL_SPACESHIP ||
6442 element == EL_SP_SNIKSNAK)
6443 DrawLevelField(x, y);
6444 else if (element == EL_MOLE)
6445 DrawLevelField(x, y);
6446 else if (element == EL_BD_BUTTERFLY ||
6447 element == EL_BD_FIREFLY)
6448 DrawLevelElementAnimationIfNeeded(x, y, element);
6449 else if (element == EL_SATELLITE)
6450 DrawLevelElementAnimationIfNeeded(x, y, element);
6451 else if (element == EL_SP_ELECTRON)
6452 DrawLevelElementAnimationIfNeeded(x, y, element);
6455 if (DONT_TOUCH(element))
6456 TestIfBadThingTouchesHero(x, y);
6459 PlayLevelSoundAction(x, y, ACTION_WAITING);
6465 InitMovingField(x, y, MovDir[x][y]);
6467 PlayLevelSoundAction(x, y, ACTION_MOVING);
6471 ContinueMoving(x, y);
6478 void ContinueMoving(int x, int y)
6480 int element = Feld[x][y];
6481 int stored = Store[x][y];
6482 struct ElementInfo *ei = &element_info[element];
6483 int direction = MovDir[x][y];
6484 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6485 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6486 int newx = x + dx, newy = y + dy;
6488 int nextx = newx + dx, nexty = newy + dy;
6491 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6492 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6494 boolean pushed_by_player = Pushed[x][y];
6496 boolean last_line = (newy == lev_fieldy - 1);
6498 MovPos[x][y] += getElementMoveStepsize(x, y);
6501 if (pushed_by_player && IS_PLAYER(x, y))
6503 /* special case: moving object pushed by player */
6504 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6507 if (pushed_by_player) /* special case: moving object pushed by player */
6508 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6511 if (ABS(MovPos[x][y]) < TILEX)
6513 DrawLevelField(x, y);
6515 return; /* element is still moving */
6518 /* element reached destination field */
6520 Feld[x][y] = EL_EMPTY;
6521 Feld[newx][newy] = element;
6522 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6525 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6527 element = Feld[newx][newy] = EL_ACID;
6530 else if (element == EL_MOLE)
6532 Feld[x][y] = EL_SAND;
6534 DrawLevelFieldCrumbledSandNeighbours(x, y);
6536 else if (element == EL_QUICKSAND_FILLING)
6538 element = Feld[newx][newy] = get_next_element(element);
6539 Store[newx][newy] = Store[x][y];
6541 else if (element == EL_QUICKSAND_EMPTYING)
6543 Feld[x][y] = get_next_element(element);
6544 element = Feld[newx][newy] = Store[x][y];
6546 else if (element == EL_MAGIC_WALL_FILLING)
6548 element = Feld[newx][newy] = get_next_element(element);
6549 if (!game.magic_wall_active)
6550 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6551 Store[newx][newy] = Store[x][y];
6553 else if (element == EL_MAGIC_WALL_EMPTYING)
6555 Feld[x][y] = get_next_element(element);
6556 if (!game.magic_wall_active)
6557 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6558 element = Feld[newx][newy] = Store[x][y];
6560 else if (element == EL_BD_MAGIC_WALL_FILLING)
6562 element = Feld[newx][newy] = get_next_element(element);
6563 if (!game.magic_wall_active)
6564 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6565 Store[newx][newy] = Store[x][y];
6567 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6569 Feld[x][y] = get_next_element(element);
6570 if (!game.magic_wall_active)
6571 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6572 element = Feld[newx][newy] = Store[x][y];
6574 else if (element == EL_AMOEBA_DROPPING)
6576 Feld[x][y] = get_next_element(element);
6577 element = Feld[newx][newy] = Store[x][y];
6579 else if (element == EL_SOKOBAN_OBJECT)
6582 Feld[x][y] = Back[x][y];
6584 if (Back[newx][newy])
6585 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6587 Back[x][y] = Back[newx][newy] = 0;
6590 else if (Store[x][y] == EL_ACID)
6592 element = Feld[newx][newy] = EL_ACID;
6596 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6597 ei->move_leave_element != EL_EMPTY &&
6598 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6599 Store[x][y] != EL_EMPTY))
6601 /* some elements can leave other elements behind after moving */
6603 Feld[x][y] = ei->move_leave_element;
6604 InitField(x, y, FALSE);
6606 if (GFX_CRUMBLED(Feld[x][y]))
6607 DrawLevelFieldCrumbledSandNeighbours(x, y);
6611 Store[x][y] = EL_EMPTY;
6615 MovDelay[newx][newy] = 0;
6617 if (CAN_CHANGE(element))
6619 /* copy element change control values to new field */
6620 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6621 ChangePage[newx][newy] = ChangePage[x][y];
6622 Changed[newx][newy] = Changed[x][y];
6623 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6626 ChangeDelay[x][y] = 0;
6627 ChangePage[x][y] = -1;
6628 Changed[x][y] = FALSE;
6629 ChangeEvent[x][y] = -1;
6631 /* copy animation control values to new field */
6632 GfxFrame[newx][newy] = GfxFrame[x][y];
6633 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6634 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6635 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6637 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6640 /* do this after checking for left-behind element */
6641 ResetGfxAnimation(x, y); /* reset animation values for old field */
6645 /* some elements can leave other elements behind after moving */
6647 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6648 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6649 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6651 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6652 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6656 int move_leave_element = ei->move_leave_element;
6658 #if USE_CHANGE_TO_TRIGGERED
6659 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6660 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6661 move_leave_element = stored;
6664 Feld[x][y] = move_leave_element;
6666 #if USE_PREVIOUS_MOVE_DIR
6667 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6668 MovDir[x][y] = direction;
6671 InitField(x, y, FALSE);
6673 if (GFX_CRUMBLED(Feld[x][y]))
6674 DrawLevelFieldCrumbledSandNeighbours(x, y);
6676 if (ELEM_IS_PLAYER(move_leave_element))
6677 RelocatePlayer(x, y, move_leave_element);
6682 /* some elements can leave other elements behind after moving */
6683 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6684 ei->move_leave_element != EL_EMPTY &&
6685 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6686 ei->can_leave_element_last))
6688 Feld[x][y] = ei->move_leave_element;
6689 InitField(x, y, FALSE);
6691 if (GFX_CRUMBLED(Feld[x][y]))
6692 DrawLevelFieldCrumbledSandNeighbours(x, y);
6695 ei->can_leave_element_last = ei->can_leave_element;
6696 ei->can_leave_element = FALSE;
6700 /* do this after checking for left-behind element */
6701 ResetGfxAnimation(x, y); /* reset animation values for old field */
6705 /* 2.1.1 (does not work correctly for spring) */
6706 if (!CAN_MOVE(element))
6707 MovDir[newx][newy] = 0;
6711 /* (does not work for falling objects that slide horizontally) */
6712 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6713 MovDir[newx][newy] = 0;
6716 if (!CAN_MOVE(element) ||
6717 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6718 MovDir[newx][newy] = 0;
6722 if (!CAN_MOVE(element) ||
6723 (CAN_FALL(element) && direction == MV_DOWN))
6724 GfxDir[x][y] = MovDir[newx][newy] = 0;
6726 if (!CAN_MOVE(element) ||
6727 (CAN_FALL(element) && direction == MV_DOWN &&
6728 (element == EL_SPRING ||
6729 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6730 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6731 GfxDir[x][y] = MovDir[newx][newy] = 0;
6737 DrawLevelField(x, y);
6738 DrawLevelField(newx, newy);
6740 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6742 /* prevent pushed element from moving on in pushed direction */
6743 if (pushed_by_player && CAN_MOVE(element) &&
6744 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6745 !(element_info[element].move_pattern & direction))
6746 TurnRound(newx, newy);
6749 /* prevent elements on conveyor belt from moving on in last direction */
6750 if (pushed_by_conveyor && CAN_FALL(element) &&
6751 direction & MV_HORIZONTAL)
6754 if (CAN_MOVE(element))
6755 InitMovDir(newx, newy);
6757 MovDir[newx][newy] = 0;
6759 MovDir[newx][newy] = 0;
6764 if (!pushed_by_player)
6766 int nextx = newx + dx, nexty = newy + dy;
6767 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6769 WasJustMoving[newx][newy] = 3;
6771 if (CAN_FALL(element) && direction == MV_DOWN)
6772 WasJustFalling[newx][newy] = 3;
6774 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6775 CheckCollision[newx][newy] = 2;
6778 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6780 TestIfBadThingTouchesHero(newx, newy);
6781 TestIfBadThingTouchesFriend(newx, newy);
6783 if (!IS_CUSTOM_ELEMENT(element))
6784 TestIfBadThingTouchesOtherBadThing(newx, newy);
6786 else if (element == EL_PENGUIN)
6787 TestIfFriendTouchesBadThing(newx, newy);
6789 #if USE_NEW_MOVE_STYLE
6791 if (CAN_FALL(element) && direction == MV_DOWN &&
6792 !last_line && IS_PLAYER(x, newy + 1))
6793 printf("::: we would now kill the player [%d]\n", FrameCounter);
6796 /* give the player one last chance (one more frame) to move away */
6797 if (CAN_FALL(element) && direction == MV_DOWN &&
6798 (last_line || (!IS_FREE(x, newy + 1) &&
6799 (!IS_PLAYER(x, newy + 1) ||
6800 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6803 if (CAN_FALL(element) && direction == MV_DOWN &&
6804 (last_line || !IS_FREE(x, newy + 1)))
6812 if (pushed_by_player && !game.use_change_when_pushing_bug)
6814 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6817 if (pushed_by_player)
6822 int dig_side = MV_DIR_OPPOSITE(direction);
6824 static int trigger_sides[4] =
6826 CH_SIDE_RIGHT, /* moving left */
6827 CH_SIDE_LEFT, /* moving right */
6828 CH_SIDE_BOTTOM, /* moving up */
6829 CH_SIDE_TOP, /* moving down */
6831 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6833 struct PlayerInfo *player = PLAYERINFO(x, y);
6835 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6836 player->index_bit, dig_side);
6837 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6838 player->index_bit, dig_side);
6843 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6847 if (ChangePage[newx][newy] != -1) /* delayed change */
6848 ChangeElement(newx, newy, ChangePage[newx][newy]);
6853 TestIfElementHitsCustomElement(newx, newy, direction);
6857 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6859 int hitting_element = Feld[newx][newy];
6861 /* !!! fix side (direction) orientation here and elsewhere !!! */
6862 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6866 if (IN_LEV_FIELD(nextx, nexty))
6868 int opposite_direction = MV_DIR_OPPOSITE(direction);
6869 int hitting_side = direction;
6870 int touched_side = opposite_direction;
6871 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6872 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6873 MovDir[nextx][nexty] != direction ||
6874 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6880 CheckElementChangeBySide(nextx, nexty, touched_element,
6881 CE_HIT_BY_SOMETHING, opposite_direction);
6883 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6884 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6886 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6888 struct ElementChangeInfo *change =
6889 &element_info[hitting_element].change_page[i];
6891 if (change->can_change &&
6892 change->has_event[CE_HITTING_X] &&
6893 change->trigger_side & touched_side &&
6894 change->trigger_element == touched_element)
6896 CheckElementChangeByPage(newx, newy, hitting_element,
6897 touched_element, CE_HITTING_X, i);
6903 if (IS_CUSTOM_ELEMENT(touched_element) &&
6904 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6906 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6908 struct ElementChangeInfo *change =
6909 &element_info[touched_element].change_page[i];
6911 if (change->can_change &&
6912 change->has_event[CE_HIT_BY_X] &&
6913 change->trigger_side & hitting_side &&
6914 change->trigger_element == hitting_element)
6916 CheckElementChangeByPage(nextx, nexty, touched_element,
6917 hitting_element, CE_HIT_BY_X,i);
6928 TestIfPlayerTouchesCustomElement(newx, newy);
6929 TestIfElementTouchesCustomElement(newx, newy);
6932 int AmoebeNachbarNr(int ax, int ay)
6935 int element = Feld[ax][ay];
6937 static int xy[4][2] =
6945 for (i = 0; i < NUM_DIRECTIONS; i++)
6947 int x = ax + xy[i][0];
6948 int y = ay + xy[i][1];
6950 if (!IN_LEV_FIELD(x, y))
6953 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6954 group_nr = AmoebaNr[x][y];
6960 void AmoebenVereinigen(int ax, int ay)
6962 int i, x, y, xx, yy;
6963 int new_group_nr = AmoebaNr[ax][ay];
6964 static int xy[4][2] =
6972 if (new_group_nr == 0)
6975 for (i = 0; i < NUM_DIRECTIONS; i++)
6980 if (!IN_LEV_FIELD(x, y))
6983 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6984 Feld[x][y] == EL_BD_AMOEBA ||
6985 Feld[x][y] == EL_AMOEBA_DEAD) &&
6986 AmoebaNr[x][y] != new_group_nr)
6988 int old_group_nr = AmoebaNr[x][y];
6990 if (old_group_nr == 0)
6993 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6994 AmoebaCnt[old_group_nr] = 0;
6995 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6996 AmoebaCnt2[old_group_nr] = 0;
6998 for (yy = 0; yy < lev_fieldy; yy++)
7000 for (xx = 0; xx < lev_fieldx; xx++)
7002 if (AmoebaNr[xx][yy] == old_group_nr)
7003 AmoebaNr[xx][yy] = new_group_nr;
7010 void AmoebeUmwandeln(int ax, int ay)
7014 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7016 int group_nr = AmoebaNr[ax][ay];
7021 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7022 printf("AmoebeUmwandeln(): This should never happen!\n");
7027 for (y = 0; y < lev_fieldy; y++)
7029 for (x = 0; x < lev_fieldx; x++)
7031 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7034 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7038 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7039 SND_AMOEBA_TURNING_TO_GEM :
7040 SND_AMOEBA_TURNING_TO_ROCK));
7045 static int xy[4][2] =
7053 for (i = 0; i < NUM_DIRECTIONS; i++)
7058 if (!IN_LEV_FIELD(x, y))
7061 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7063 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7064 SND_AMOEBA_TURNING_TO_GEM :
7065 SND_AMOEBA_TURNING_TO_ROCK));
7072 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7075 int group_nr = AmoebaNr[ax][ay];
7076 boolean done = FALSE;
7081 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7082 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7087 for (y = 0; y < lev_fieldy; y++)
7089 for (x = 0; x < lev_fieldx; x++)
7091 if (AmoebaNr[x][y] == group_nr &&
7092 (Feld[x][y] == EL_AMOEBA_DEAD ||
7093 Feld[x][y] == EL_BD_AMOEBA ||
7094 Feld[x][y] == EL_AMOEBA_GROWING))
7097 Feld[x][y] = new_element;
7098 InitField(x, y, FALSE);
7099 DrawLevelField(x, y);
7106 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7107 SND_BD_AMOEBA_TURNING_TO_ROCK :
7108 SND_BD_AMOEBA_TURNING_TO_GEM));
7111 void AmoebeWaechst(int x, int y)
7113 static unsigned long sound_delay = 0;
7114 static unsigned long sound_delay_value = 0;
7116 if (!MovDelay[x][y]) /* start new growing cycle */
7120 if (DelayReached(&sound_delay, sound_delay_value))
7123 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7125 if (Store[x][y] == EL_BD_AMOEBA)
7126 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7128 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7130 sound_delay_value = 30;
7134 if (MovDelay[x][y]) /* wait some time before growing bigger */
7137 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7139 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7140 6 - MovDelay[x][y]);
7142 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7145 if (!MovDelay[x][y])
7147 Feld[x][y] = Store[x][y];
7149 DrawLevelField(x, y);
7154 void AmoebaDisappearing(int x, int y)
7156 static unsigned long sound_delay = 0;
7157 static unsigned long sound_delay_value = 0;
7159 if (!MovDelay[x][y]) /* start new shrinking cycle */
7163 if (DelayReached(&sound_delay, sound_delay_value))
7164 sound_delay_value = 30;
7167 if (MovDelay[x][y]) /* wait some time before shrinking */
7170 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7172 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7173 6 - MovDelay[x][y]);
7175 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7178 if (!MovDelay[x][y])
7180 Feld[x][y] = EL_EMPTY;
7181 DrawLevelField(x, y);
7183 /* don't let mole enter this field in this cycle;
7184 (give priority to objects falling to this field from above) */
7190 void AmoebeAbleger(int ax, int ay)
7193 int element = Feld[ax][ay];
7194 int graphic = el2img(element);
7195 int newax = ax, neway = ay;
7196 static int xy[4][2] =
7204 if (!level.amoeba_speed)
7206 Feld[ax][ay] = EL_AMOEBA_DEAD;
7207 DrawLevelField(ax, ay);
7211 if (IS_ANIMATED(graphic))
7212 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7214 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7215 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7217 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7220 if (MovDelay[ax][ay])
7224 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7227 int x = ax + xy[start][0];
7228 int y = ay + xy[start][1];
7230 if (!IN_LEV_FIELD(x, y))
7234 if (IS_FREE(x, y) ||
7235 CAN_GROW_INTO(Feld[x][y]) ||
7236 Feld[x][y] == EL_QUICKSAND_EMPTY)
7242 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7243 if (IS_FREE(x, y) ||
7244 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7251 if (newax == ax && neway == ay)
7254 else /* normal or "filled" (BD style) amoeba */
7257 boolean waiting_for_player = FALSE;
7259 for (i = 0; i < NUM_DIRECTIONS; i++)
7261 int j = (start + i) % 4;
7262 int x = ax + xy[j][0];
7263 int y = ay + xy[j][1];
7265 if (!IN_LEV_FIELD(x, y))
7269 if (IS_FREE(x, y) ||
7270 CAN_GROW_INTO(Feld[x][y]) ||
7271 Feld[x][y] == EL_QUICKSAND_EMPTY)
7278 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7279 if (IS_FREE(x, y) ||
7280 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7287 else if (IS_PLAYER(x, y))
7288 waiting_for_player = TRUE;
7291 if (newax == ax && neway == ay) /* amoeba cannot grow */
7294 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7296 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7299 Feld[ax][ay] = EL_AMOEBA_DEAD;
7300 DrawLevelField(ax, ay);
7301 AmoebaCnt[AmoebaNr[ax][ay]]--;
7303 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7305 if (element == EL_AMOEBA_FULL)
7306 AmoebeUmwandeln(ax, ay);
7307 else if (element == EL_BD_AMOEBA)
7308 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7313 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7315 /* amoeba gets larger by growing in some direction */
7317 int new_group_nr = AmoebaNr[ax][ay];
7320 if (new_group_nr == 0)
7322 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7323 printf("AmoebeAbleger(): This should never happen!\n");
7328 AmoebaNr[newax][neway] = new_group_nr;
7329 AmoebaCnt[new_group_nr]++;
7330 AmoebaCnt2[new_group_nr]++;
7332 /* if amoeba touches other amoeba(s) after growing, unify them */
7333 AmoebenVereinigen(newax, neway);
7335 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7337 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7343 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7344 (neway == lev_fieldy - 1 && newax != ax))
7346 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7347 Store[newax][neway] = element;
7349 else if (neway == ay)
7351 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7353 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7355 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7360 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7361 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7362 Store[ax][ay] = EL_AMOEBA_DROP;
7363 ContinueMoving(ax, ay);
7367 DrawLevelField(newax, neway);
7370 void Life(int ax, int ay)
7373 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7375 int element = Feld[ax][ay];
7376 int graphic = el2img(element);
7377 boolean changed = FALSE;
7379 if (IS_ANIMATED(graphic))
7380 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7385 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7386 MovDelay[ax][ay] = life_time;
7388 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7391 if (MovDelay[ax][ay])
7395 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7397 int xx = ax+x1, yy = ay+y1;
7400 if (!IN_LEV_FIELD(xx, yy))
7403 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7405 int x = xx+x2, y = yy+y2;
7407 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7410 if (((Feld[x][y] == element ||
7411 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7413 (IS_FREE(x, y) && Stop[x][y]))
7417 if (xx == ax && yy == ay) /* field in the middle */
7419 if (nachbarn < life[0] || nachbarn > life[1])
7421 Feld[xx][yy] = EL_EMPTY;
7423 DrawLevelField(xx, yy);
7424 Stop[xx][yy] = TRUE;
7429 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7430 { /* free border field */
7431 if (nachbarn >= life[2] && nachbarn <= life[3])
7433 Feld[xx][yy] = element;
7434 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7436 DrawLevelField(xx, yy);
7437 Stop[xx][yy] = TRUE;
7442 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7443 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7444 { /* free border field */
7445 if (nachbarn >= life[2] && nachbarn <= life[3])
7447 Feld[xx][yy] = element;
7448 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7450 DrawLevelField(xx, yy);
7451 Stop[xx][yy] = TRUE;
7459 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7460 SND_GAME_OF_LIFE_GROWING);
7463 static void InitRobotWheel(int x, int y)
7465 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7468 static void RunRobotWheel(int x, int y)
7470 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7473 static void StopRobotWheel(int x, int y)
7475 if (ZX == x && ZY == y)
7479 static void InitTimegateWheel(int x, int y)
7482 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7484 /* another brainless, "type style" bug ... :-( */
7485 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7489 static void RunTimegateWheel(int x, int y)
7491 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7494 void CheckExit(int x, int y)
7496 if (local_player->gems_still_needed > 0 ||
7497 local_player->sokobanfields_still_needed > 0 ||
7498 local_player->lights_still_needed > 0)
7500 int element = Feld[x][y];
7501 int graphic = el2img(element);
7503 if (IS_ANIMATED(graphic))
7504 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7509 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7512 Feld[x][y] = EL_EXIT_OPENING;
7514 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7517 void CheckExitSP(int x, int y)
7519 if (local_player->gems_still_needed > 0)
7521 int element = Feld[x][y];
7522 int graphic = el2img(element);
7524 if (IS_ANIMATED(graphic))
7525 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7530 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7533 Feld[x][y] = EL_SP_EXIT_OPENING;
7535 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7538 static void CloseAllOpenTimegates()
7542 for (y = 0; y < lev_fieldy; y++)
7544 for (x = 0; x < lev_fieldx; x++)
7546 int element = Feld[x][y];
7548 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7550 Feld[x][y] = EL_TIMEGATE_CLOSING;
7552 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7554 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7561 void EdelsteinFunkeln(int x, int y)
7563 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7566 if (Feld[x][y] == EL_BD_DIAMOND)
7569 if (MovDelay[x][y] == 0) /* next animation frame */
7570 MovDelay[x][y] = 11 * !SimpleRND(500);
7572 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7576 if (setup.direct_draw && MovDelay[x][y])
7577 SetDrawtoField(DRAW_BUFFERED);
7579 DrawLevelElementAnimation(x, y, Feld[x][y]);
7581 if (MovDelay[x][y] != 0)
7583 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7584 10 - MovDelay[x][y]);
7586 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7588 if (setup.direct_draw)
7592 dest_x = FX + SCREENX(x) * TILEX;
7593 dest_y = FY + SCREENY(y) * TILEY;
7595 BlitBitmap(drawto_field, window,
7596 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7597 SetDrawtoField(DRAW_DIRECT);
7603 void MauerWaechst(int x, int y)
7607 if (!MovDelay[x][y]) /* next animation frame */
7608 MovDelay[x][y] = 3 * delay;
7610 if (MovDelay[x][y]) /* wait some time before next frame */
7614 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7616 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7617 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7619 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7622 if (!MovDelay[x][y])
7624 if (MovDir[x][y] == MV_LEFT)
7626 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7627 DrawLevelField(x - 1, y);
7629 else if (MovDir[x][y] == MV_RIGHT)
7631 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7632 DrawLevelField(x + 1, y);
7634 else if (MovDir[x][y] == MV_UP)
7636 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7637 DrawLevelField(x, y - 1);
7641 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7642 DrawLevelField(x, y + 1);
7645 Feld[x][y] = Store[x][y];
7647 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7648 DrawLevelField(x, y);
7653 void MauerAbleger(int ax, int ay)
7655 int element = Feld[ax][ay];
7656 int graphic = el2img(element);
7657 boolean oben_frei = FALSE, unten_frei = FALSE;
7658 boolean links_frei = FALSE, rechts_frei = FALSE;
7659 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7660 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7661 boolean new_wall = FALSE;
7663 if (IS_ANIMATED(graphic))
7664 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7666 if (!MovDelay[ax][ay]) /* start building new wall */
7667 MovDelay[ax][ay] = 6;
7669 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7672 if (MovDelay[ax][ay])
7676 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7678 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7680 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7682 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7685 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7686 element == EL_EXPANDABLE_WALL_ANY)
7690 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7691 Store[ax][ay-1] = element;
7692 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7693 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7694 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7695 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7700 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7701 Store[ax][ay+1] = element;
7702 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7703 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7704 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7705 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7710 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7711 element == EL_EXPANDABLE_WALL_ANY ||
7712 element == EL_EXPANDABLE_WALL)
7716 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7717 Store[ax-1][ay] = element;
7718 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7719 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7720 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7721 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7727 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7728 Store[ax+1][ay] = element;
7729 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7730 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7731 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7732 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7737 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7738 DrawLevelField(ax, ay);
7740 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7742 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7743 unten_massiv = TRUE;
7744 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7745 links_massiv = TRUE;
7746 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7747 rechts_massiv = TRUE;
7749 if (((oben_massiv && unten_massiv) ||
7750 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7751 element == EL_EXPANDABLE_WALL) &&
7752 ((links_massiv && rechts_massiv) ||
7753 element == EL_EXPANDABLE_WALL_VERTICAL))
7754 Feld[ax][ay] = EL_WALL;
7758 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7760 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7764 void CheckForDragon(int x, int y)
7767 boolean dragon_found = FALSE;
7768 static int xy[4][2] =
7776 for (i = 0; i < NUM_DIRECTIONS; i++)
7778 for (j = 0; j < 4; j++)
7780 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7782 if (IN_LEV_FIELD(xx, yy) &&
7783 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7785 if (Feld[xx][yy] == EL_DRAGON)
7786 dragon_found = TRUE;
7795 for (i = 0; i < NUM_DIRECTIONS; i++)
7797 for (j = 0; j < 3; j++)
7799 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7801 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7803 Feld[xx][yy] = EL_EMPTY;
7804 DrawLevelField(xx, yy);
7813 static void InitBuggyBase(int x, int y)
7815 int element = Feld[x][y];
7816 int activating_delay = FRAMES_PER_SECOND / 4;
7819 (element == EL_SP_BUGGY_BASE ?
7820 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7821 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7823 element == EL_SP_BUGGY_BASE_ACTIVE ?
7824 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7827 static void WarnBuggyBase(int x, int y)
7830 static int xy[4][2] =
7838 for (i = 0; i < NUM_DIRECTIONS; i++)
7840 int xx = x + xy[i][0], yy = y + xy[i][1];
7842 if (IS_PLAYER(xx, yy))
7844 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7851 static void InitTrap(int x, int y)
7853 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7856 static void ActivateTrap(int x, int y)
7858 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7861 static void ChangeActiveTrap(int x, int y)
7863 int graphic = IMG_TRAP_ACTIVE;
7865 /* if new animation frame was drawn, correct crumbled sand border */
7866 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7867 DrawLevelFieldCrumbledSand(x, y);
7870 static int getSpecialActionElement(int element, int number, int base_element)
7872 return (element != EL_EMPTY ? element :
7873 number != -1 ? base_element + number - 1 :
7877 static int getModifiedActionNumber(int value_old, int value_min, int value_max,
7878 int operator, int operand)
7880 int value_new = (operator == CA_MODE_ADD ? value_old + operand :
7881 operator == CA_MODE_SUBTRACT ? value_old - operand :
7882 operator == CA_MODE_MULTIPLY ? value_old * operand :
7883 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7884 operator == CA_MODE_SET ? operand :
7887 return (value_new < value_min ? value_min :
7888 value_new > value_max ? value_max :
7892 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7894 struct ElementInfo *ei = &element_info[element];
7895 struct ElementChangeInfo *change = &ei->change_page[page];
7896 int action_type = change->action_type;
7897 int action_mode = change->action_mode;
7898 int action_arg = change->action_arg;
7901 /* ---------- determine action paramater values ---------- */
7903 int action_arg_element =
7904 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7905 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7906 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7909 int action_arg_number =
7910 (action_arg <= CA_ARG_MAX ? action_arg :
7911 action_arg == CA_ARG_NUMBER_MIN ? CA_ARG_MIN :
7912 action_arg == CA_ARG_NUMBER_MAX ? CA_ARG_MAX :
7913 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7914 action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count :
7915 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
7918 /* (for explicit player choice, set invalid value to "no player") */
7919 int action_arg_player_bits =
7920 (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
7921 action_arg >= CA_ARG_PLAYER_1 &&
7922 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7923 action_arg >= CA_ARG_1 &&
7924 action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
7925 action_arg_element >= EL_PLAYER_1 &&
7926 action_arg_element <= EL_PLAYER_4 ?
7927 (1 << (action_arg_element - EL_PLAYER_1)) :
7930 /* (for implicit player choice, set invalid value to "all players") */
7931 int trigger_player_bits =
7932 (change->actual_trigger_player >= EL_PLAYER_1 &&
7933 change->actual_trigger_player <= EL_PLAYER_4 ?
7934 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7937 /* ---------- execute action ---------- */
7946 case CA_EXIT_PLAYER:
7948 for (i = 0; i < MAX_PLAYERS; i++)
7949 if (action_arg_player_bits & (1 << i))
7950 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7955 case CA_KILL_PLAYER:
7957 for (i = 0; i < MAX_PLAYERS; i++)
7958 if (action_arg_player_bits & (1 << i))
7959 KillHero(&stored_player[i]);
7964 case CA_RESTART_LEVEL:
7966 printf("::: CA_RESTART_LEVEL -- not yet implemented\n");
7971 case CA_SHOW_ENVELOPE:
7973 int element = getSpecialActionElement(action_arg_element,
7974 action_arg_number, EL_ENVELOPE_1);
7976 if (IS_ENVELOPE(element))
7977 local_player->show_envelope = element;
7984 int element = getSpecialActionElement(action_arg_element,
7985 action_arg_number, EL_KEY_1);
7987 if (IS_KEY(element))
7989 for (i = 0; i < MAX_PLAYERS; i++)
7991 if (trigger_player_bits & (1 << i))
7993 stored_player[i].key[KEY_NR(element)] = TRUE;
7995 DrawGameValue_Keys(stored_player[i].key);
7997 redraw_mask |= REDRAW_DOOR_1;
8007 int element = getSpecialActionElement(action_arg_element,
8008 action_arg_number, EL_KEY_1);
8010 if (IS_KEY(element))
8012 for (i = 0; i < MAX_PLAYERS; i++)
8014 if (trigger_player_bits & (1 << i))
8016 stored_player[i].key[KEY_NR(element)] = FALSE;
8018 DrawGameValue_Keys(stored_player[i].key);
8020 redraw_mask |= REDRAW_DOOR_1;
8028 case CA_SET_PLAYER_SPEED:
8030 for (i = 0; i < MAX_PLAYERS; i++)
8032 if (trigger_player_bits & (1 << i))
8034 if (action_arg == CA_ARG_NUMBER_RESET)
8035 stored_player[i].move_delay_value = game.initial_move_delay_value;
8036 else if (action_arg == CA_ARG_NUMBER_NORMAL)
8037 stored_player[i].move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8038 else if (action_arg == CA_ARG_NUMBER_MIN)
8039 stored_player[i].move_delay_value = 16;
8040 else if (action_arg == CA_ARG_NUMBER_MAX)
8041 stored_player[i].move_delay_value = MOVE_DELAY_HIGH_SPEED;
8045 if (action_mode == CA_MODE_ADD)
8047 action_mode = CA_MODE_DIVIDE;
8048 action_arg_number = (1 << action_arg_number);
8050 else if (action_mode == CA_MODE_SUBTRACT)
8052 action_mode = CA_MODE_MULTIPLY;
8053 action_arg_number = (1 << action_arg_number);
8056 int mode = (action_mode == CA_MODE_MULTIPLY ? CA_MODE_DIVIDE :
8057 action_mode == CA_MODE_DIVIDE ? CA_MODE_MULTIPLY :
8060 stored_player[i].move_delay_value =
8061 getModifiedActionNumber(stored_player[i].move_delay_value,
8063 action_mode, action_arg_number);
8074 local_player->gems_still_needed =
8075 getModifiedActionNumber(local_player->gems_still_needed, 0, 999,
8076 action_mode, action_arg_number);
8078 DrawGameValue_Emeralds(local_player->gems_still_needed);
8085 if (level.time > 0) /* only modify limited time value */
8087 TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999,
8088 action_mode, action_arg_number);
8090 DrawGameValue_Time(TimeLeft);
8098 local_player->score =
8099 getModifiedActionNumber(local_player->score, 0, 99999,
8100 action_mode, action_arg_number);
8102 DrawGameValue_Score(local_player->score);
8107 case CA_SET_CE_SCORE:
8109 printf("::: CA_SET_CE_SCORE -- not yet implemented\n");
8114 case CA_SET_CE_COUNT:
8116 printf("::: CA_SET_CE_COUNT -- not yet implemented\n");
8121 case CA_SET_DYNABOMB_NUMBER:
8123 printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
8128 case CA_SET_DYNABOMB_SIZE:
8130 printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
8135 case CA_SET_DYNABOMB_POWER:
8137 printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
8142 case CA_TOGGLE_PLAYER_GRAVITY:
8144 game.gravity = !game.gravity;
8149 case CA_ENABLE_PLAYER_GRAVITY:
8151 game.gravity = TRUE;
8156 case CA_DISABLE_PLAYER_GRAVITY:
8158 game.gravity = FALSE;
8168 static void ChangeElementNowExt(struct ElementChangeInfo *change,
8169 int x, int y, int target_element)
8171 int previous_move_direction = MovDir[x][y];
8173 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
8174 IS_WALKABLE(Feld[x][y]));
8176 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
8177 IS_WALKABLE(Feld[x][y]) &&
8181 /* check if element under player changes from accessible to unaccessible
8182 (needed for special case of dropping element which then changes) */
8183 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8184 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
8187 printf("::: BOOOM! [%d, '%s']\n", target_element,
8188 element_info[target_element].token_name);
8200 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8201 RemoveMovingField(x, y);
8205 Feld[x][y] = target_element;
8208 Feld[x][y] = target_element;
8211 ResetGfxAnimation(x, y);
8212 ResetRandomAnimationValue(x, y);
8214 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8215 MovDir[x][y] = previous_move_direction;
8218 InitField_WithBug1(x, y, FALSE);
8220 InitField(x, y, FALSE);
8221 if (CAN_MOVE(Feld[x][y]))
8225 DrawLevelField(x, y);
8227 if (GFX_CRUMBLED(Feld[x][y]))
8228 DrawLevelFieldCrumbledSandNeighbours(x, y);
8232 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
8236 TestIfBadThingTouchesHero(x, y);
8237 TestIfPlayerTouchesCustomElement(x, y);
8238 TestIfElementTouchesCustomElement(x, y);
8241 /* "Changed[][]" not set yet to allow "entered by player" change one time */
8242 if (ELEM_IS_PLAYER(target_element))
8243 RelocatePlayer(x, y, target_element);
8246 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
8248 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
8252 TestIfBadThingTouchesHero(x, y);
8253 TestIfPlayerTouchesCustomElement(x, y);
8254 TestIfElementTouchesCustomElement(x, y);
8258 if (change->use_action)
8259 ExecuteCustomElementAction(...);
8263 static boolean ChangeElementNow(int x, int y, int element, int page)
8265 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8267 int old_element = Feld[x][y];
8269 /* always use default change event to prevent running into a loop */
8270 if (ChangeEvent[x][y] == -1)
8271 ChangeEvent[x][y] = CE_DELAY;
8273 if (ChangeEvent[x][y] == CE_DELAY)
8275 /* reset actual trigger element, trigger player and action element */
8276 change->actual_trigger_element = EL_EMPTY;
8277 change->actual_trigger_player = EL_PLAYER_1;
8281 /* do not change any elements that have already changed in this frame */
8285 /* do not change already changed elements with same change event */
8286 if (Changed[x][y] & ChangeEvent[x][y])
8291 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
8293 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
8297 /* !!! indirect change before direct change !!! */
8298 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
8301 if (change->explode)
8308 if (change->use_target_content)
8310 boolean complete_replace = TRUE;
8311 boolean can_replace[3][3];
8314 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8317 boolean is_walkable;
8318 boolean is_diggable;
8319 boolean is_collectible;
8320 boolean is_removable;
8321 boolean is_destructible;
8322 int ex = x + xx - 1;
8323 int ey = y + yy - 1;
8324 int content_element = change->target_content[xx][yy];
8327 can_replace[xx][yy] = TRUE;
8329 if (ex == x && ey == y) /* do not check changing element itself */
8332 if (content_element == EL_EMPTY_SPACE)
8334 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8339 if (!IN_LEV_FIELD(ex, ey))
8341 can_replace[xx][yy] = FALSE;
8342 complete_replace = FALSE;
8348 if (Changed[ex][ey]) /* do not change already changed elements */
8350 can_replace[xx][yy] = FALSE;
8351 complete_replace = FALSE;
8359 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8360 e = MovingOrBlocked2Element(ex, ey);
8365 is_empty = (IS_FREE(ex, ey) ||
8366 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8367 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8368 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8372 is_empty = (IS_FREE(ex, ey) ||
8373 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8375 is_empty = (IS_FREE(ex, ey) ||
8376 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8381 is_walkable = (is_empty || IS_WALKABLE(e));
8382 is_diggable = (is_empty || IS_DIGGABLE(e));
8383 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8384 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8385 is_removable = (is_diggable || is_collectible);
8387 can_replace[xx][yy] =
8388 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8389 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8390 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8391 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8392 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8393 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8394 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8396 if (!can_replace[xx][yy])
8397 complete_replace = FALSE;
8399 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8400 IS_WALKABLE(content_element)));
8402 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8404 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8407 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8408 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8409 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8411 can_replace[xx][yy] = FALSE;
8412 complete_replace = FALSE;
8417 if (!change->only_if_complete || complete_replace)
8419 boolean something_has_changed = FALSE;
8421 if (change->only_if_complete && change->use_random_replace &&
8422 RND(100) < change->random_percentage)
8425 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8427 int ex = x + xx - 1;
8428 int ey = y + yy - 1;
8429 int content_element;
8431 if (can_replace[xx][yy] && (!change->use_random_replace ||
8432 RND(100) < change->random_percentage))
8434 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8435 RemoveMovingField(ex, ey);
8437 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8439 content_element = change->target_content[xx][yy];
8440 target_element = GET_TARGET_ELEMENT(content_element, change);
8442 ChangeElementNowExt(change, ex, ey, target_element);
8444 something_has_changed = TRUE;
8446 /* for symmetry reasons, freeze newly created border elements */
8447 if (ex != x || ey != y)
8448 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8452 if (something_has_changed)
8453 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8458 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8460 ChangeElementNowExt(change, x, y, target_element);
8462 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8466 /* this uses direct change before indirect change */
8467 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8473 static void ChangeElement(int x, int y, int page)
8475 int element = MovingOrBlocked2Element(x, y);
8476 struct ElementInfo *ei = &element_info[element];
8477 struct ElementChangeInfo *change = &ei->change_page[page];
8480 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8483 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8484 x, y, element, element_info[element].token_name);
8485 printf("ChangeElement(): This should never happen!\n");
8490 /* this can happen with classic bombs on walkable, changing elements */
8491 if (!CAN_CHANGE(element))
8494 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8495 ChangeDelay[x][y] = 0;
8501 if (ChangeDelay[x][y] == 0) /* initialize element change */
8504 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8506 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8507 RND(change->delay_random * change->delay_frames)) + 1;
8510 ResetGfxAnimation(x, y);
8511 ResetRandomAnimationValue(x, y);
8513 if (change->pre_change_function)
8514 change->pre_change_function(x, y);
8517 ChangeDelay[x][y]--;
8519 if (ChangeDelay[x][y] != 0) /* continue element change */
8521 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8523 if (IS_ANIMATED(graphic))
8524 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8526 if (change->change_function)
8527 change->change_function(x, y);
8529 else /* finish element change */
8531 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8533 page = ChangePage[x][y];
8534 ChangePage[x][y] = -1;
8536 change = &ei->change_page[page];
8540 if (IS_MOVING(x, y) && !change->explode)
8542 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8545 ChangeDelay[x][y] = 1; /* try change after next move step */
8546 ChangePage[x][y] = page; /* remember page to use for change */
8552 if (change->use_action)
8553 ExecuteCustomElementAction(x, y, element, page);
8556 if (ChangeElementNow(x, y, element, page))
8558 if (change->post_change_function)
8559 change->post_change_function(x, y);
8564 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8565 int trigger_element,
8572 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8574 if (!(trigger_events[trigger_element][trigger_event]))
8577 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8579 int element = EL_CUSTOM_START + i;
8581 boolean change_element = FALSE;
8584 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8587 for (j = 0; j < element_info[element].num_change_pages; j++)
8589 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8591 if (change->can_change &&
8592 change->has_event[trigger_event] &&
8593 change->trigger_side & trigger_side &&
8594 change->trigger_player & trigger_player &&
8595 change->trigger_page & trigger_page_bits &&
8596 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8599 if (!(change->has_event[trigger_event]))
8600 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8601 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8604 change_element = TRUE;
8607 change->actual_trigger_element = trigger_element;
8608 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8614 if (!change_element)
8617 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8620 if (x == lx && y == ly) /* do not change trigger element itself */
8624 if (Feld[x][y] == element)
8626 ChangeDelay[x][y] = 1;
8627 ChangeEvent[x][y] = trigger_event;
8628 ChangeElement(x, y, page);
8636 static boolean CheckElementChangeExt(int x, int y,
8638 int trigger_element,
8644 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8647 if (Feld[x][y] == EL_BLOCKED)
8649 Blocked2Moving(x, y, &x, &y);
8650 element = Feld[x][y];
8654 if (Feld[x][y] != element) /* check if element has already changed */
8657 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8658 Feld[x][y], element_info[Feld[x][y]].token_name,
8659 element, element_info[element].token_name,
8668 if (trigger_page < 0)
8670 boolean change_element = FALSE;
8673 for (i = 0; i < element_info[element].num_change_pages; i++)
8675 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8677 if (change->can_change &&
8678 change->has_event[trigger_event] &&
8679 change->trigger_side & trigger_side &&
8680 change->trigger_player & trigger_player)
8682 change_element = TRUE;
8685 change->actual_trigger_element = trigger_element;
8686 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8692 if (!change_element)
8697 struct ElementInfo *ei = &element_info[element];
8698 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8700 change->actual_trigger_element = trigger_element;
8701 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8706 /* !!! this check misses pages with same event, but different side !!! */
8708 if (trigger_page < 0)
8709 trigger_page = element_info[element].event_page_nr[trigger_event];
8711 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8715 ChangeDelay[x][y] = 1;
8716 ChangeEvent[x][y] = trigger_event;
8717 ChangeElement(x, y, trigger_page);
8722 static void PlayPlayerSound(struct PlayerInfo *player)
8724 int jx = player->jx, jy = player->jy;
8725 int element = player->element_nr;
8726 int last_action = player->last_action_waiting;
8727 int action = player->action_waiting;
8729 if (player->is_waiting)
8731 if (action != last_action)
8732 PlayLevelSoundElementAction(jx, jy, element, action);
8734 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8738 if (action != last_action)
8739 StopSound(element_info[element].sound[last_action]);
8741 if (last_action == ACTION_SLEEPING)
8742 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8746 static void PlayAllPlayersSound()
8750 for (i = 0; i < MAX_PLAYERS; i++)
8751 if (stored_player[i].active)
8752 PlayPlayerSound(&stored_player[i]);
8755 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8757 boolean last_waiting = player->is_waiting;
8758 int move_dir = player->MovDir;
8760 player->last_action_waiting = player->action_waiting;
8764 if (!last_waiting) /* not waiting -> waiting */
8766 player->is_waiting = TRUE;
8768 player->frame_counter_bored =
8770 game.player_boring_delay_fixed +
8771 SimpleRND(game.player_boring_delay_random);
8772 player->frame_counter_sleeping =
8774 game.player_sleeping_delay_fixed +
8775 SimpleRND(game.player_sleeping_delay_random);
8777 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8780 if (game.player_sleeping_delay_fixed +
8781 game.player_sleeping_delay_random > 0 &&
8782 player->anim_delay_counter == 0 &&
8783 player->post_delay_counter == 0 &&
8784 FrameCounter >= player->frame_counter_sleeping)
8785 player->is_sleeping = TRUE;
8786 else if (game.player_boring_delay_fixed +
8787 game.player_boring_delay_random > 0 &&
8788 FrameCounter >= player->frame_counter_bored)
8789 player->is_bored = TRUE;
8791 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8792 player->is_bored ? ACTION_BORING :
8795 if (player->is_sleeping)
8797 if (player->num_special_action_sleeping > 0)
8799 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8801 int last_special_action = player->special_action_sleeping;
8802 int num_special_action = player->num_special_action_sleeping;
8803 int special_action =
8804 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8805 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8806 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8807 last_special_action + 1 : ACTION_SLEEPING);
8808 int special_graphic =
8809 el_act_dir2img(player->element_nr, special_action, move_dir);
8811 player->anim_delay_counter =
8812 graphic_info[special_graphic].anim_delay_fixed +
8813 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8814 player->post_delay_counter =
8815 graphic_info[special_graphic].post_delay_fixed +
8816 SimpleRND(graphic_info[special_graphic].post_delay_random);
8818 player->special_action_sleeping = special_action;
8821 if (player->anim_delay_counter > 0)
8823 player->action_waiting = player->special_action_sleeping;
8824 player->anim_delay_counter--;
8826 else if (player->post_delay_counter > 0)
8828 player->post_delay_counter--;
8832 else if (player->is_bored)
8834 if (player->num_special_action_bored > 0)
8836 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8838 int special_action =
8839 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8840 int special_graphic =
8841 el_act_dir2img(player->element_nr, special_action, move_dir);
8843 player->anim_delay_counter =
8844 graphic_info[special_graphic].anim_delay_fixed +
8845 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8846 player->post_delay_counter =
8847 graphic_info[special_graphic].post_delay_fixed +
8848 SimpleRND(graphic_info[special_graphic].post_delay_random);
8850 player->special_action_bored = special_action;
8853 if (player->anim_delay_counter > 0)
8855 player->action_waiting = player->special_action_bored;
8856 player->anim_delay_counter--;
8858 else if (player->post_delay_counter > 0)
8860 player->post_delay_counter--;
8865 else if (last_waiting) /* waiting -> not waiting */
8867 player->is_waiting = FALSE;
8868 player->is_bored = FALSE;
8869 player->is_sleeping = FALSE;
8871 player->frame_counter_bored = -1;
8872 player->frame_counter_sleeping = -1;
8874 player->anim_delay_counter = 0;
8875 player->post_delay_counter = 0;
8877 player->action_waiting = ACTION_DEFAULT;
8879 player->special_action_bored = ACTION_DEFAULT;
8880 player->special_action_sleeping = ACTION_DEFAULT;
8885 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8888 static byte stored_player_action[MAX_PLAYERS];
8889 static int num_stored_actions = 0;
8891 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8892 int left = player_action & JOY_LEFT;
8893 int right = player_action & JOY_RIGHT;
8894 int up = player_action & JOY_UP;
8895 int down = player_action & JOY_DOWN;
8896 int button1 = player_action & JOY_BUTTON_1;
8897 int button2 = player_action & JOY_BUTTON_2;
8898 int dx = (left ? -1 : right ? 1 : 0);
8899 int dy = (up ? -1 : down ? 1 : 0);
8902 stored_player_action[player->index_nr] = 0;
8903 num_stored_actions++;
8907 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8910 if (!player->active || tape.pausing)
8914 printf("::: [%d %d %d %d] [%d %d]\n",
8915 left, right, up, down, button1, button2);
8921 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8926 if (player->MovPos == 0)
8927 CheckGravityMovement(player);
8930 snapped = SnapField(player, dx, dy);
8934 dropped = DropElement(player);
8936 moved = MovePlayer(player, dx, dy);
8939 if (tape.single_step && tape.recording && !tape.pausing)
8941 if (button1 || (dropped && !moved))
8943 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8944 SnapField(player, 0, 0); /* stop snapping */
8948 SetPlayerWaiting(player, FALSE);
8951 return player_action;
8953 stored_player_action[player->index_nr] = player_action;
8959 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8962 /* no actions for this player (no input at player's configured device) */
8964 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8965 SnapField(player, 0, 0);
8966 CheckGravityMovementWhenNotMoving(player);
8968 if (player->MovPos == 0)
8969 SetPlayerWaiting(player, TRUE);
8971 if (player->MovPos == 0) /* needed for tape.playing */
8972 player->is_moving = FALSE;
8974 player->is_dropping = FALSE;
8980 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8982 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8984 TapeRecordAction(stored_player_action);
8985 num_stored_actions = 0;
8992 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8994 static byte stored_player_action[MAX_PLAYERS];
8995 static int num_stored_actions = 0;
8996 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8997 int left = player_action & JOY_LEFT;
8998 int right = player_action & JOY_RIGHT;
8999 int up = player_action & JOY_UP;
9000 int down = player_action & JOY_DOWN;
9001 int button1 = player_action & JOY_BUTTON_1;
9002 int button2 = player_action & JOY_BUTTON_2;
9003 int dx = (left ? -1 : right ? 1 : 0);
9004 int dy = (up ? -1 : down ? 1 : 0);
9006 stored_player_action[player->index_nr] = 0;
9007 num_stored_actions++;
9009 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
9011 if (!player->active || tape.pausing)
9016 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
9019 snapped = SnapField(player, dx, dy);
9023 dropped = DropElement(player);
9025 moved = MovePlayer(player, dx, dy);
9028 if (tape.single_step && tape.recording && !tape.pausing)
9030 if (button1 || (dropped && !moved))
9032 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9033 SnapField(player, 0, 0); /* stop snapping */
9037 stored_player_action[player->index_nr] = player_action;
9041 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
9043 /* no actions for this player (no input at player's configured device) */
9045 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9046 SnapField(player, 0, 0);
9047 CheckGravityMovementWhenNotMoving(player);
9049 if (player->MovPos == 0)
9050 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
9052 if (player->MovPos == 0) /* needed for tape.playing */
9053 player->is_moving = FALSE;
9056 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
9058 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
9060 TapeRecordAction(stored_player_action);
9061 num_stored_actions = 0;
9066 void AdvanceFrameAndPlayerCounters(int player_nr)
9070 /* advance frame counters (global frame counter and time frame counter) */
9074 /* advance player counters (counters for move delay, move animation etc.) */
9075 for (i = 0; i < MAX_PLAYERS; i++)
9077 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9079 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9081 if (!advance_player_counters) /* not all players may be affected */
9084 stored_player[i].Frame += move_frames;
9086 if (stored_player[i].MovPos != 0)
9087 stored_player[i].StepFrame += move_frames;
9089 #if USE_NEW_MOVE_DELAY
9090 if (stored_player[i].move_delay > 0)
9091 stored_player[i].move_delay--;
9094 #if USE_NEW_PUSH_DELAY
9095 /* due to bugs in previous versions, counter must count up, not down */
9096 if (stored_player[i].push_delay != -1)
9097 stored_player[i].push_delay++;
9100 if (stored_player[i].drop_delay > 0)
9101 stored_player[i].drop_delay--;
9107 static unsigned long game_frame_delay = 0;
9108 unsigned long game_frame_delay_value;
9109 int magic_wall_x = 0, magic_wall_y = 0;
9110 int i, x, y, element, graphic;
9111 byte *recorded_player_action;
9112 byte summarized_player_action = 0;
9114 byte tape_action[MAX_PLAYERS];
9117 if (game_status != GAME_MODE_PLAYING)
9120 game_frame_delay_value =
9121 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9123 if (tape.playing && tape.warp_forward && !tape.pausing)
9124 game_frame_delay_value = 0;
9126 /* ---------- main game synchronization point ---------- */
9128 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9130 if (network_playing && !network_player_action_received)
9134 printf("DEBUG: try to get network player actions in time\n");
9138 #if defined(NETWORK_AVALIABLE)
9139 /* last chance to get network player actions without main loop delay */
9143 if (game_status != GAME_MODE_PLAYING)
9146 if (!network_player_action_received)
9150 printf("DEBUG: failed to get network player actions in time\n");
9161 printf("::: getting new tape action [%d]\n", FrameCounter);
9164 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9167 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
9168 if (recorded_player_action == NULL && tape.pausing)
9173 printf("::: %d\n", stored_player[0].action);
9177 if (recorded_player_action != NULL)
9178 for (i = 0; i < MAX_PLAYERS; i++)
9179 stored_player[i].action = recorded_player_action[i];
9182 for (i = 0; i < MAX_PLAYERS; i++)
9184 summarized_player_action |= stored_player[i].action;
9186 if (!network_playing)
9187 stored_player[i].effective_action = stored_player[i].action;
9190 #if defined(NETWORK_AVALIABLE)
9191 if (network_playing)
9192 SendToServer_MovePlayer(summarized_player_action);
9195 if (!options.network && !setup.team_mode)
9196 local_player->effective_action = summarized_player_action;
9199 if (recorded_player_action != NULL)
9200 for (i = 0; i < MAX_PLAYERS; i++)
9201 stored_player[i].effective_action = recorded_player_action[i];
9205 for (i = 0; i < MAX_PLAYERS; i++)
9207 tape_action[i] = stored_player[i].effective_action;
9209 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9210 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9213 /* only save actions from input devices, but not programmed actions */
9215 TapeRecordAction(tape_action);
9218 for (i = 0; i < MAX_PLAYERS; i++)
9220 int actual_player_action = stored_player[i].effective_action;
9223 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9224 - rnd_equinox_tetrachloride 048
9225 - rnd_equinox_tetrachloride_ii 096
9226 - rnd_emanuel_schmieg 002
9227 - doctor_sloan_ww 001, 020
9229 if (stored_player[i].MovPos == 0)
9230 CheckGravityMovement(&stored_player[i]);
9234 /* overwrite programmed action with tape action */
9235 if (stored_player[i].programmed_action)
9236 actual_player_action = stored_player[i].programmed_action;
9240 if (stored_player[i].programmed_action)
9241 printf("::: %d\n", stored_player[i].programmed_action);
9244 if (recorded_player_action)
9247 if (stored_player[i].programmed_action &&
9248 stored_player[i].programmed_action != recorded_player_action[i])
9249 printf("::: %d: %d <-> %d\n", i,
9250 stored_player[i].programmed_action, recorded_player_action[i]);
9254 actual_player_action = recorded_player_action[i];
9259 /* overwrite tape action with programmed action */
9260 if (stored_player[i].programmed_action)
9261 actual_player_action = stored_player[i].programmed_action;
9266 printf("::: action: %d: %x [%d]\n",
9267 stored_player[i].MovPos, actual_player_action, FrameCounter);
9271 PlayerActions(&stored_player[i], actual_player_action);
9273 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9275 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9276 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9279 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9284 TapeRecordAction(tape_action);
9287 network_player_action_received = FALSE;
9289 ScrollScreen(NULL, SCROLL_GO_ON);
9295 for (i = 0; i < MAX_PLAYERS; i++)
9296 stored_player[i].Frame++;
9300 /* for backwards compatibility, the following code emulates a fixed bug that
9301 occured when pushing elements (causing elements that just made their last
9302 pushing step to already (if possible) make their first falling step in the
9303 same game frame, which is bad); this code is also needed to use the famous
9304 "spring push bug" which is used in older levels and might be wanted to be
9305 used also in newer levels, but in this case the buggy pushing code is only
9306 affecting the "spring" element and no other elements */
9309 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9311 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9314 for (i = 0; i < MAX_PLAYERS; i++)
9316 struct PlayerInfo *player = &stored_player[i];
9321 if (player->active && player->is_pushing && player->is_moving &&
9323 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9324 Feld[x][y] == EL_SPRING))
9326 if (player->active && player->is_pushing && player->is_moving &&
9330 ContinueMoving(x, y);
9332 /* continue moving after pushing (this is actually a bug) */
9333 if (!IS_MOVING(x, y))
9342 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9344 Changed[x][y] = FALSE;
9345 ChangeEvent[x][y] = -1;
9347 #if USE_NEW_BLOCK_STYLE
9348 /* this must be handled before main playfield loop */
9349 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9352 if (MovDelay[x][y] <= 0)
9358 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9360 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9361 printf("GameActions(): This should never happen!\n");
9363 ChangePage[x][y] = -1;
9368 if (WasJustMoving[x][y] > 0)
9369 WasJustMoving[x][y]--;
9370 if (WasJustFalling[x][y] > 0)
9371 WasJustFalling[x][y]--;
9372 if (CheckCollision[x][y] > 0)
9373 CheckCollision[x][y]--;
9378 /* reset finished pushing action (not done in ContinueMoving() to allow
9379 continous pushing animation for elements with zero push delay) */
9380 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9382 ResetGfxAnimation(x, y);
9383 DrawLevelField(x, y);
9388 if (IS_BLOCKED(x, y))
9392 Blocked2Moving(x, y, &oldx, &oldy);
9393 if (!IS_MOVING(oldx, oldy))
9395 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9396 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9397 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9398 printf("GameActions(): This should never happen!\n");
9404 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9406 element = Feld[x][y];
9408 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9410 graphic = el2img(element);
9416 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9418 element = graphic = 0;
9422 if (graphic_info[graphic].anim_global_sync)
9423 GfxFrame[x][y] = FrameCounter;
9425 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9426 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9427 ResetRandomAnimationValue(x, y);
9429 SetRandomAnimationValue(x, y);
9432 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9435 if (IS_INACTIVE(element))
9437 if (IS_ANIMATED(graphic))
9438 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9444 /* this may take place after moving, so 'element' may have changed */
9446 if (IS_CHANGING(x, y))
9448 if (IS_CHANGING(x, y) &&
9449 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9453 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9454 element_info[element].event_page_nr[CE_DELAY]);
9456 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9459 element = Feld[x][y];
9460 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9464 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9469 element = Feld[x][y];
9470 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9472 if (element == EL_MOLE)
9473 printf("::: %d, %d, %d [%d]\n",
9474 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9478 if (element == EL_YAMYAM)
9479 printf("::: %d, %d, %d\n",
9480 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9484 if (IS_ANIMATED(graphic) &&
9488 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9491 if (element == EL_BUG)
9492 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9496 if (element == EL_MOLE)
9497 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9501 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9502 EdelsteinFunkeln(x, y);
9504 else if ((element == EL_ACID ||
9505 element == EL_EXIT_OPEN ||
9506 element == EL_SP_EXIT_OPEN ||
9507 element == EL_SP_TERMINAL ||
9508 element == EL_SP_TERMINAL_ACTIVE ||
9509 element == EL_EXTRA_TIME ||
9510 element == EL_SHIELD_NORMAL ||
9511 element == EL_SHIELD_DEADLY) &&
9512 IS_ANIMATED(graphic))
9513 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9514 else if (IS_MOVING(x, y))
9515 ContinueMoving(x, y);
9516 else if (IS_ACTIVE_BOMB(element))
9517 CheckDynamite(x, y);
9519 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9520 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9522 else if (element == EL_AMOEBA_GROWING)
9523 AmoebeWaechst(x, y);
9524 else if (element == EL_AMOEBA_SHRINKING)
9525 AmoebaDisappearing(x, y);
9527 #if !USE_NEW_AMOEBA_CODE
9528 else if (IS_AMOEBALIVE(element))
9529 AmoebeAbleger(x, y);
9532 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9534 else if (element == EL_EXIT_CLOSED)
9536 else if (element == EL_SP_EXIT_CLOSED)
9538 else if (element == EL_EXPANDABLE_WALL_GROWING)
9540 else if (element == EL_EXPANDABLE_WALL ||
9541 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9542 element == EL_EXPANDABLE_WALL_VERTICAL ||
9543 element == EL_EXPANDABLE_WALL_ANY)
9545 else if (element == EL_FLAMES)
9546 CheckForDragon(x, y);
9548 else if (IS_AUTO_CHANGING(element))
9549 ChangeElement(x, y);
9551 else if (element == EL_EXPLOSION)
9552 ; /* drawing of correct explosion animation is handled separately */
9553 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9554 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9557 /* this may take place after moving, so 'element' may have changed */
9558 if (IS_AUTO_CHANGING(Feld[x][y]))
9559 ChangeElement(x, y);
9562 if (IS_BELT_ACTIVE(element))
9563 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9565 if (game.magic_wall_active)
9567 int jx = local_player->jx, jy = local_player->jy;
9569 /* play the element sound at the position nearest to the player */
9570 if ((element == EL_MAGIC_WALL_FULL ||
9571 element == EL_MAGIC_WALL_ACTIVE ||
9572 element == EL_MAGIC_WALL_EMPTYING ||
9573 element == EL_BD_MAGIC_WALL_FULL ||
9574 element == EL_BD_MAGIC_WALL_ACTIVE ||
9575 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9576 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9584 #if USE_NEW_AMOEBA_CODE
9585 /* new experimental amoeba growth stuff */
9587 if (!(FrameCounter % 8))
9590 static unsigned long random = 1684108901;
9592 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9595 x = (random >> 10) % lev_fieldx;
9596 y = (random >> 20) % lev_fieldy;
9598 x = RND(lev_fieldx);
9599 y = RND(lev_fieldy);
9601 element = Feld[x][y];
9604 if (!IS_PLAYER(x,y) &&
9605 (element == EL_EMPTY ||
9606 CAN_GROW_INTO(element) ||
9607 element == EL_QUICKSAND_EMPTY ||
9608 element == EL_ACID_SPLASH_LEFT ||
9609 element == EL_ACID_SPLASH_RIGHT))
9611 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9612 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9613 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9614 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9615 Feld[x][y] = EL_AMOEBA_DROP;
9618 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9619 if (!IS_PLAYER(x,y) &&
9620 (element == EL_EMPTY ||
9621 element == EL_SAND ||
9622 element == EL_QUICKSAND_EMPTY ||
9623 element == EL_ACID_SPLASH_LEFT ||
9624 element == EL_ACID_SPLASH_RIGHT))
9626 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9627 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9628 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9629 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9630 Feld[x][y] = EL_AMOEBA_DROP;
9634 random = random * 129 + 1;
9640 if (game.explosions_delayed)
9643 game.explosions_delayed = FALSE;
9645 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9647 element = Feld[x][y];
9649 if (ExplodeField[x][y])
9650 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9651 else if (element == EL_EXPLOSION)
9652 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9654 ExplodeField[x][y] = EX_TYPE_NONE;
9657 game.explosions_delayed = TRUE;
9660 if (game.magic_wall_active)
9662 if (!(game.magic_wall_time_left % 4))
9664 int element = Feld[magic_wall_x][magic_wall_y];
9666 if (element == EL_BD_MAGIC_WALL_FULL ||
9667 element == EL_BD_MAGIC_WALL_ACTIVE ||
9668 element == EL_BD_MAGIC_WALL_EMPTYING)
9669 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9671 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9674 if (game.magic_wall_time_left > 0)
9676 game.magic_wall_time_left--;
9677 if (!game.magic_wall_time_left)
9679 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9681 element = Feld[x][y];
9683 if (element == EL_MAGIC_WALL_ACTIVE ||
9684 element == EL_MAGIC_WALL_FULL)
9686 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9687 DrawLevelField(x, y);
9689 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9690 element == EL_BD_MAGIC_WALL_FULL)
9692 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9693 DrawLevelField(x, y);
9697 game.magic_wall_active = FALSE;
9702 if (game.light_time_left > 0)
9704 game.light_time_left--;
9706 if (game.light_time_left == 0)
9707 RedrawAllLightSwitchesAndInvisibleElements();
9710 if (game.timegate_time_left > 0)
9712 game.timegate_time_left--;
9714 if (game.timegate_time_left == 0)
9715 CloseAllOpenTimegates();
9718 for (i = 0; i < MAX_PLAYERS; i++)
9720 struct PlayerInfo *player = &stored_player[i];
9722 if (SHIELD_ON(player))
9724 if (player->shield_deadly_time_left)
9725 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9726 else if (player->shield_normal_time_left)
9727 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9731 if (TimeFrames >= FRAMES_PER_SECOND)
9736 for (i = 0; i < MAX_PLAYERS; i++)
9738 struct PlayerInfo *player = &stored_player[i];
9740 if (SHIELD_ON(player))
9742 player->shield_normal_time_left--;
9744 if (player->shield_deadly_time_left > 0)
9745 player->shield_deadly_time_left--;
9749 if (!level.use_step_counter)
9757 if (TimeLeft <= 10 && setup.time_limit)
9758 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9760 DrawGameValue_Time(TimeLeft);
9762 if (!TimeLeft && setup.time_limit)
9763 for (i = 0; i < MAX_PLAYERS; i++)
9764 KillHero(&stored_player[i]);
9766 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9767 DrawGameValue_Time(TimePlayed);
9770 if (tape.recording || tape.playing)
9771 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9775 PlayAllPlayersSound();
9777 if (options.debug) /* calculate frames per second */
9779 static unsigned long fps_counter = 0;
9780 static int fps_frames = 0;
9781 unsigned long fps_delay_ms = Counter() - fps_counter;
9785 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9787 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9790 fps_counter = Counter();
9793 redraw_mask |= REDRAW_FPS;
9797 if (stored_player[0].jx != stored_player[0].last_jx ||
9798 stored_player[0].jy != stored_player[0].last_jy)
9799 printf("::: %d, %d, %d, %d, %d\n",
9800 stored_player[0].MovDir,
9801 stored_player[0].MovPos,
9802 stored_player[0].GfxPos,
9803 stored_player[0].Frame,
9804 stored_player[0].StepFrame);
9807 #if USE_NEW_MOVE_DELAY
9808 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9813 for (i = 0; i < MAX_PLAYERS; i++)
9816 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9818 stored_player[i].Frame += move_frames;
9820 if (stored_player[i].MovPos != 0)
9821 stored_player[i].StepFrame += move_frames;
9823 #if USE_NEW_MOVE_DELAY
9824 if (stored_player[i].move_delay > 0)
9825 stored_player[i].move_delay--;
9828 if (stored_player[i].drop_delay > 0)
9829 stored_player[i].drop_delay--;
9834 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9836 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9838 local_player->show_envelope = 0;
9842 #if USE_NEW_RANDOMIZE
9843 /* use random number generator in every frame to make it less predictable */
9844 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9849 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9851 int min_x = x, min_y = y, max_x = x, max_y = y;
9854 for (i = 0; i < MAX_PLAYERS; i++)
9856 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9858 if (!stored_player[i].active || &stored_player[i] == player)
9861 min_x = MIN(min_x, jx);
9862 min_y = MIN(min_y, jy);
9863 max_x = MAX(max_x, jx);
9864 max_y = MAX(max_y, jy);
9867 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9870 static boolean AllPlayersInVisibleScreen()
9874 for (i = 0; i < MAX_PLAYERS; i++)
9876 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9878 if (!stored_player[i].active)
9881 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9888 void ScrollLevel(int dx, int dy)
9890 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9893 BlitBitmap(drawto_field, drawto_field,
9894 FX + TILEX * (dx == -1) - softscroll_offset,
9895 FY + TILEY * (dy == -1) - softscroll_offset,
9896 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9897 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9898 FX + TILEX * (dx == 1) - softscroll_offset,
9899 FY + TILEY * (dy == 1) - softscroll_offset);
9903 x = (dx == 1 ? BX1 : BX2);
9904 for (y = BY1; y <= BY2; y++)
9905 DrawScreenField(x, y);
9910 y = (dy == 1 ? BY1 : BY2);
9911 for (x = BX1; x <= BX2; x++)
9912 DrawScreenField(x, y);
9915 redraw_mask |= REDRAW_FIELD;
9919 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9921 int nextx = x + dx, nexty = y + dy;
9922 int element = Feld[x][y];
9925 element != EL_SP_PORT_LEFT &&
9926 element != EL_SP_GRAVITY_PORT_LEFT &&
9927 element != EL_SP_PORT_HORIZONTAL &&
9928 element != EL_SP_PORT_ANY) ||
9930 element != EL_SP_PORT_RIGHT &&
9931 element != EL_SP_GRAVITY_PORT_RIGHT &&
9932 element != EL_SP_PORT_HORIZONTAL &&
9933 element != EL_SP_PORT_ANY) ||
9935 element != EL_SP_PORT_UP &&
9936 element != EL_SP_GRAVITY_PORT_UP &&
9937 element != EL_SP_PORT_VERTICAL &&
9938 element != EL_SP_PORT_ANY) ||
9940 element != EL_SP_PORT_DOWN &&
9941 element != EL_SP_GRAVITY_PORT_DOWN &&
9942 element != EL_SP_PORT_VERTICAL &&
9943 element != EL_SP_PORT_ANY) ||
9944 !IN_LEV_FIELD(nextx, nexty) ||
9945 !IS_FREE(nextx, nexty))
9952 static boolean canFallDown(struct PlayerInfo *player)
9954 int jx = player->jx, jy = player->jy;
9956 return (IN_LEV_FIELD(jx, jy + 1) &&
9957 (IS_FREE(jx, jy + 1) ||
9958 #if USE_NEW_BLOCK_STYLE
9959 #if USE_GRAVITY_BUGFIX_OLD
9960 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9963 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9964 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9965 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9968 static boolean canPassField(int x, int y, int move_dir)
9970 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9971 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9972 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9975 int element = Feld[x][y];
9977 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9978 !CAN_MOVE(element) &&
9979 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9980 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9981 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9984 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9986 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9987 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9988 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9992 int nextx = newx + dx;
9993 int nexty = newy + dy;
9997 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9998 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10000 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
10002 (IS_DIGGABLE(Feld[newx][newy]) ||
10003 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10004 canPassField(newx, newy, move_dir)));
10007 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10008 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10009 (IS_DIGGABLE(Feld[newx][newy]) ||
10010 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10011 canPassField(newx, newy, move_dir)));
10014 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10015 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
10016 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10017 canPassField(newx, newy, move_dir)));
10019 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10020 (IS_DIGGABLE(Feld[newx][newy]) ||
10021 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10022 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
10023 !CAN_MOVE(Feld[newx][newy]) &&
10024 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10025 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10026 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
10032 static void CheckGravityMovement(struct PlayerInfo *player)
10034 if (game.gravity && !player->programmed_action)
10037 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10038 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10040 int move_dir_horizontal = player->action & MV_HORIZONTAL;
10041 int move_dir_vertical = player->action & MV_VERTICAL;
10045 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10047 boolean player_is_snapping = player->action & JOY_BUTTON_1;
10050 int jx = player->jx, jy = player->jy;
10052 boolean player_is_moving_to_valid_field =
10053 (!player_is_snapping &&
10054 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10055 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10059 (player->last_move_dir & MV_HORIZONTAL ?
10060 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
10061 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
10065 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10066 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10067 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10068 int new_jx = jx + dx, new_jy = jy + dy;
10069 int nextx = new_jx + dx, nexty = new_jy + dy;
10075 boolean player_can_fall_down = canFallDown(player);
10077 boolean player_can_fall_down =
10078 (IN_LEV_FIELD(jx, jy + 1) &&
10079 (IS_FREE(jx, jy + 1) ||
10080 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
10084 boolean player_can_fall_down =
10085 (IN_LEV_FIELD(jx, jy + 1) &&
10086 (IS_FREE(jx, jy + 1)));
10090 boolean player_is_moving_to_valid_field =
10093 !player_is_snapping &&
10097 IN_LEV_FIELD(new_jx, new_jy) &&
10098 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
10099 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
10100 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
10101 IN_LEV_FIELD(nextx, nexty) &&
10102 element_info[Feld[nextx][nexty]].access_direction & move_dir))
10104 IN_LEV_FIELD(new_jx, new_jy) &&
10105 (Feld[new_jx][new_jy] == EL_SP_BASE ||
10106 Feld[new_jx][new_jy] == EL_SAND ||
10107 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
10108 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
10109 /* !!! extend EL_SAND to anything diggable !!! */
10115 boolean player_is_standing_on_valid_field =
10116 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10117 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
10121 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
10122 player_can_fall_down,
10123 player_is_standing_on_valid_field,
10124 player_is_moving_to_valid_field,
10125 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
10126 player->effective_action,
10127 player->can_fall_into_acid);
10130 if (player_can_fall_down &&
10132 !player_is_standing_on_valid_field &&
10134 !player_is_moving_to_valid_field)
10137 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
10138 jx, jy, FrameCounter);
10141 player->programmed_action = MV_DOWN;
10146 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10149 return CheckGravityMovement(player);
10152 if (game.gravity && !player->programmed_action)
10154 int jx = player->jx, jy = player->jy;
10155 boolean field_under_player_is_free =
10156 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10157 boolean player_is_standing_on_valid_field =
10158 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10159 (IS_WALKABLE(Feld[jx][jy]) &&
10160 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10162 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10163 player->programmed_action = MV_DOWN;
10168 MovePlayerOneStep()
10169 -----------------------------------------------------------------------------
10170 dx, dy: direction (non-diagonal) to try to move the player to
10171 real_dx, real_dy: direction as read from input device (can be diagonal)
10174 boolean MovePlayerOneStep(struct PlayerInfo *player,
10175 int dx, int dy, int real_dx, int real_dy)
10178 static int trigger_sides[4][2] =
10180 /* enter side leave side */
10181 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10182 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10183 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10184 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10186 int move_direction = (dx == -1 ? MV_LEFT :
10187 dx == +1 ? MV_RIGHT :
10189 dy == +1 ? MV_DOWN : MV_NO_MOVING);
10190 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10191 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10193 int jx = player->jx, jy = player->jy;
10194 int new_jx = jx + dx, new_jy = jy + dy;
10198 if (!player->active || (!dx && !dy))
10199 return MF_NO_ACTION;
10201 player->MovDir = (dx < 0 ? MV_LEFT :
10202 dx > 0 ? MV_RIGHT :
10204 dy > 0 ? MV_DOWN : MV_NO_MOVING);
10206 if (!IN_LEV_FIELD(new_jx, new_jy))
10207 return MF_NO_ACTION;
10209 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10210 return MF_NO_ACTION;
10213 element = MovingOrBlocked2Element(new_jx, new_jy);
10215 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10218 if (DONT_RUN_INTO(element))
10220 if (element == EL_ACID && dx == 0 && dy == 1)
10222 SplashAcid(new_jx, new_jy);
10223 Feld[jx][jy] = EL_PLAYER_1;
10224 InitMovingField(jx, jy, MV_DOWN);
10225 Store[jx][jy] = EL_ACID;
10226 ContinueMoving(jx, jy);
10230 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
10235 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10236 if (can_move != MF_MOVING)
10239 /* check if DigField() has caused relocation of the player */
10240 if (player->jx != jx || player->jy != jy)
10241 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
10243 StorePlayer[jx][jy] = 0;
10244 player->last_jx = jx;
10245 player->last_jy = jy;
10246 player->jx = new_jx;
10247 player->jy = new_jy;
10248 StorePlayer[new_jx][new_jy] = player->element_nr;
10251 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10253 player->step_counter++;
10256 player->drop_delay = 0;
10259 PlayerVisit[jx][jy] = FrameCounter;
10261 ScrollPlayer(player, SCROLL_INIT);
10264 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
10266 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
10268 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
10271 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
10273 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
10274 CE_PLAYER_ENTERS_X, enter_side);
10275 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
10276 CE_ENTERED_BY_PLAYER, enter_side);
10283 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10285 int jx = player->jx, jy = player->jy;
10286 int old_jx = jx, old_jy = jy;
10287 int moved = MF_NO_ACTION;
10290 if (!player->active)
10295 if (player->MovPos == 0)
10297 player->is_moving = FALSE;
10298 player->is_digging = FALSE;
10299 player->is_collecting = FALSE;
10300 player->is_snapping = FALSE;
10301 player->is_pushing = FALSE;
10307 if (!player->active || (!dx && !dy))
10312 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10320 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
10321 player->move_delay + player->move_delay_value);
10324 #if USE_NEW_MOVE_DELAY
10325 if (player->move_delay > 0)
10327 if (!FrameReached(&player->move_delay, player->move_delay_value))
10331 printf("::: can NOT move\n");
10337 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10338 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10345 printf("::: COULD move now\n");
10348 #if USE_NEW_MOVE_DELAY
10349 player->move_delay = -1; /* set to "uninitialized" value */
10352 /* store if player is automatically moved to next field */
10353 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10355 /* remove the last programmed player action */
10356 player->programmed_action = 0;
10358 if (player->MovPos)
10360 /* should only happen if pre-1.2 tape recordings are played */
10361 /* this is only for backward compatibility */
10363 int original_move_delay_value = player->move_delay_value;
10366 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10370 /* scroll remaining steps with finest movement resolution */
10371 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10373 while (player->MovPos)
10375 ScrollPlayer(player, SCROLL_GO_ON);
10376 ScrollScreen(NULL, SCROLL_GO_ON);
10378 #if USE_NEW_MOVE_DELAY
10379 AdvanceFrameAndPlayerCounters(player->index_nr);
10388 player->move_delay_value = original_move_delay_value;
10391 if (player->last_move_dir & MV_HORIZONTAL)
10393 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10394 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10398 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10399 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10405 if (moved & MF_MOVING && !ScreenMovPos &&
10406 (player == local_player || !options.network))
10408 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10409 int offset = (setup.scroll_delay ? 3 : 0);
10411 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10413 /* actual player has left the screen -- scroll in that direction */
10414 if (jx != old_jx) /* player has moved horizontally */
10415 scroll_x += (jx - old_jx);
10416 else /* player has moved vertically */
10417 scroll_y += (jy - old_jy);
10421 if (jx != old_jx) /* player has moved horizontally */
10423 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10424 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10425 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10427 /* don't scroll over playfield boundaries */
10428 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10429 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10431 /* don't scroll more than one field at a time */
10432 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10434 /* don't scroll against the player's moving direction */
10435 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10436 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10437 scroll_x = old_scroll_x;
10439 else /* player has moved vertically */
10441 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10442 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10443 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10445 /* don't scroll over playfield boundaries */
10446 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10447 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10449 /* don't scroll more than one field at a time */
10450 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10452 /* don't scroll against the player's moving direction */
10453 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10454 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10455 scroll_y = old_scroll_y;
10459 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10461 if (!options.network && !AllPlayersInVisibleScreen())
10463 scroll_x = old_scroll_x;
10464 scroll_y = old_scroll_y;
10468 ScrollScreen(player, SCROLL_INIT);
10469 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10476 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10478 if (!(moved & MF_MOVING) && !player->is_pushing)
10483 player->StepFrame = 0;
10485 if (moved & MF_MOVING)
10488 printf("::: REALLY moves now\n");
10491 if (old_jx != jx && old_jy == jy)
10492 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10493 else if (old_jx == jx && old_jy != jy)
10494 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10496 DrawLevelField(jx, jy); /* for "crumbled sand" */
10498 player->last_move_dir = player->MovDir;
10499 player->is_moving = TRUE;
10501 player->is_snapping = FALSE;
10505 player->is_switching = FALSE;
10508 player->is_dropping = FALSE;
10512 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10515 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10518 int move_direction = player->MovDir;
10520 int enter_side = MV_DIR_OPPOSITE(move_direction);
10521 int leave_side = move_direction;
10523 static int trigger_sides[4][2] =
10525 /* enter side leave side */
10526 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10527 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10528 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10529 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10531 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10532 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10534 int old_element = Feld[old_jx][old_jy];
10535 int new_element = Feld[jx][jy];
10538 /* !!! TEST ONLY !!! */
10539 if (IS_CUSTOM_ELEMENT(old_element))
10540 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10542 player->index_bit, leave_side);
10544 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10545 CE_PLAYER_LEAVES_X,
10546 player->index_bit, leave_side);
10548 if (IS_CUSTOM_ELEMENT(new_element))
10549 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10550 player->index_bit, enter_side);
10552 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10553 CE_PLAYER_ENTERS_X,
10554 player->index_bit, enter_side);
10564 CheckGravityMovementWhenNotMoving(player);
10567 player->last_move_dir = MV_NO_MOVING;
10569 player->is_moving = FALSE;
10571 #if USE_NEW_MOVE_STYLE
10572 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10573 /* ensure that the player is also allowed to move in the next frame */
10574 /* (currently, the player is forced to wait eight frames before he can try
10577 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10578 player->move_delay = 0; /* allow direct movement in the next frame */
10582 #if USE_NEW_MOVE_DELAY
10583 if (player->move_delay == -1) /* not yet initialized by DigField() */
10584 player->move_delay = player->move_delay_value;
10587 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10589 TestIfHeroTouchesBadThing(jx, jy);
10590 TestIfPlayerTouchesCustomElement(jx, jy);
10593 if (!player->active)
10594 RemoveHero(player);
10599 void ScrollPlayer(struct PlayerInfo *player, int mode)
10601 int jx = player->jx, jy = player->jy;
10602 int last_jx = player->last_jx, last_jy = player->last_jy;
10603 int move_stepsize = TILEX / player->move_delay_value;
10605 if (!player->active || !player->MovPos)
10608 if (mode == SCROLL_INIT)
10610 player->actual_frame_counter = FrameCounter;
10611 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10614 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10616 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10617 player->block_delay);
10620 #if USE_NEW_BLOCK_STYLE
10623 if (player->block_delay <= 0)
10624 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10627 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10628 Feld[last_jx][last_jy] == EL_EMPTY)
10630 int last_field_block_delay = 0; /* start with no blocking at all */
10631 int block_delay_adjustment = player->block_delay_adjustment;
10633 /* if player blocks last field, add delay for exactly one move */
10634 if (player->block_last_field)
10636 last_field_block_delay += player->move_delay_value;
10638 #if USE_GRAVITY_BUGFIX_NEW
10639 /* when blocking enabled, prevent moving up despite gravity */
10640 if (game.gravity && player->MovDir == MV_UP)
10641 block_delay_adjustment = -1;
10645 /* add block delay adjustment (also possible when not blocking) */
10646 last_field_block_delay += block_delay_adjustment;
10649 #if USE_BLOCK_DELAY_BUGFIX
10650 /* when blocking enabled, correct block delay for fast movement */
10651 if (player->block_last_field &&
10652 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10653 last_field_block_delay =
10654 player->move_delay_value + player->block_delay_adjustment;
10659 #if USE_GRAVITY_BUGFIX_NEW
10660 /* when blocking enabled, correct block delay for gravity movement */
10661 if (player->block_last_field &&
10662 game.gravity && player->MovDir == MV_UP)
10663 last_field_block_delay = player->move_delay_value - 1;
10667 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10668 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10671 #if USE_NEW_MOVE_STYLE
10672 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10673 player->block_last_field) &&
10674 Feld[last_jx][last_jy] == EL_EMPTY)
10675 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10677 if (Feld[last_jx][last_jy] == EL_EMPTY)
10678 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10683 DrawPlayer(player);
10688 else if (!FrameReached(&player->actual_frame_counter, 1))
10691 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10692 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10694 #if USE_NEW_BLOCK_STYLE
10696 if (!player->block_last_field &&
10697 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10699 RemoveField(last_jx, last_jy);
10701 Feld[last_jx][last_jy] = EL_EMPTY;
10705 /* before DrawPlayer() to draw correct player graphic for this case */
10706 if (player->MovPos == 0)
10707 CheckGravityMovement(player);
10710 DrawPlayer(player); /* needed here only to cleanup last field */
10713 if (player->MovPos == 0) /* player reached destination field */
10716 if (player->move_delay_reset_counter > 0)
10718 player->move_delay_reset_counter--;
10720 if (player->move_delay_reset_counter == 0)
10722 /* continue with normal speed after quickly moving through gate */
10723 HALVE_PLAYER_SPEED(player);
10725 /* be able to make the next move without delay */
10726 player->move_delay = 0;
10730 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10732 /* continue with normal speed after quickly moving through gate */
10733 HALVE_PLAYER_SPEED(player);
10735 /* be able to make the next move without delay */
10736 player->move_delay = 0;
10740 #if USE_NEW_BLOCK_STYLE
10742 if (player->block_last_field &&
10743 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10745 RemoveField(last_jx, last_jy);
10747 Feld[last_jx][last_jy] = EL_EMPTY;
10751 player->last_jx = jx;
10752 player->last_jy = jy;
10754 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10755 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10756 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10758 DrawPlayer(player); /* needed here only to cleanup last field */
10759 RemoveHero(player);
10761 if (local_player->friends_still_needed == 0 ||
10762 IS_SP_ELEMENT(Feld[jx][jy]))
10763 player->LevelSolved = player->GameOver = TRUE;
10767 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10768 /* this breaks one level: "machine", level 000 */
10770 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10773 int move_direction = player->MovDir;
10775 int enter_side = MV_DIR_OPPOSITE(move_direction);
10776 int leave_side = move_direction;
10778 static int trigger_sides[4][2] =
10780 /* enter side leave side */
10781 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10782 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10783 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10784 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10786 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10787 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10789 int old_jx = last_jx;
10790 int old_jy = last_jy;
10791 int old_element = Feld[old_jx][old_jy];
10792 int new_element = Feld[jx][jy];
10795 /* !!! TEST ONLY !!! */
10796 if (IS_CUSTOM_ELEMENT(old_element))
10797 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10799 player->index_bit, leave_side);
10801 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10802 CE_PLAYER_LEAVES_X,
10803 player->index_bit, leave_side);
10805 if (IS_CUSTOM_ELEMENT(new_element))
10806 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10807 player->index_bit, enter_side);
10809 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10810 CE_PLAYER_ENTERS_X,
10811 player->index_bit, enter_side);
10817 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10819 TestIfHeroTouchesBadThing(jx, jy);
10820 TestIfPlayerTouchesCustomElement(jx, jy);
10823 /* needed because pushed element has not yet reached its destination,
10824 so it would trigger a change event at its previous field location */
10825 if (!player->is_pushing)
10827 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10830 if (!player->active)
10831 RemoveHero(player);
10834 if (level.use_step_counter)
10844 if (TimeLeft <= 10 && setup.time_limit)
10845 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10847 DrawGameValue_Time(TimeLeft);
10849 if (!TimeLeft && setup.time_limit)
10850 for (i = 0; i < MAX_PLAYERS; i++)
10851 KillHero(&stored_player[i]);
10853 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10854 DrawGameValue_Time(TimePlayed);
10857 if (tape.single_step && tape.recording && !tape.pausing &&
10858 !player->programmed_action)
10859 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10863 void ScrollScreen(struct PlayerInfo *player, int mode)
10865 static unsigned long screen_frame_counter = 0;
10867 if (mode == SCROLL_INIT)
10869 /* set scrolling step size according to actual player's moving speed */
10870 ScrollStepSize = TILEX / player->move_delay_value;
10872 screen_frame_counter = FrameCounter;
10873 ScreenMovDir = player->MovDir;
10874 ScreenMovPos = player->MovPos;
10875 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10878 else if (!FrameReached(&screen_frame_counter, 1))
10883 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10884 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10885 redraw_mask |= REDRAW_FIELD;
10888 ScreenMovDir = MV_NO_MOVING;
10891 void TestIfPlayerTouchesCustomElement(int x, int y)
10893 static int xy[4][2] =
10900 static int trigger_sides[4][2] =
10902 /* center side border side */
10903 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10904 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10905 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10906 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10908 static int touch_dir[4] =
10910 MV_LEFT | MV_RIGHT,
10915 int center_element = Feld[x][y]; /* should always be non-moving! */
10918 for (i = 0; i < NUM_DIRECTIONS; i++)
10920 int xx = x + xy[i][0];
10921 int yy = y + xy[i][1];
10922 int center_side = trigger_sides[i][0];
10923 int border_side = trigger_sides[i][1];
10924 int border_element;
10926 if (!IN_LEV_FIELD(xx, yy))
10929 if (IS_PLAYER(x, y))
10931 struct PlayerInfo *player = PLAYERINFO(x, y);
10933 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10934 border_element = Feld[xx][yy]; /* may be moving! */
10935 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10936 border_element = Feld[xx][yy];
10937 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10938 border_element = MovingOrBlocked2Element(xx, yy);
10940 continue; /* center and border element do not touch */
10943 /* !!! TEST ONLY !!! */
10944 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10945 player->index_bit, border_side);
10946 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10947 CE_PLAYER_TOUCHES_X,
10948 player->index_bit, border_side);
10950 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10951 CE_PLAYER_TOUCHES_X,
10952 player->index_bit, border_side);
10953 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10954 player->index_bit, border_side);
10957 else if (IS_PLAYER(xx, yy))
10959 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10961 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10963 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10964 continue; /* center and border element do not touch */
10968 /* !!! TEST ONLY !!! */
10969 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10970 player->index_bit, center_side);
10971 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10972 CE_PLAYER_TOUCHES_X,
10973 player->index_bit, center_side);
10975 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10976 CE_PLAYER_TOUCHES_X,
10977 player->index_bit, center_side);
10978 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10979 player->index_bit, center_side);
10987 void TestIfElementTouchesCustomElement(int x, int y)
10989 static int xy[4][2] =
10996 static int trigger_sides[4][2] =
10998 /* center side border side */
10999 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11000 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11001 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11002 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11004 static int touch_dir[4] =
11006 MV_LEFT | MV_RIGHT,
11011 boolean change_center_element = FALSE;
11012 int center_element_change_page = 0;
11013 int center_element = Feld[x][y]; /* should always be non-moving! */
11014 int border_trigger_element = EL_UNDEFINED;
11017 for (i = 0; i < NUM_DIRECTIONS; i++)
11019 int xx = x + xy[i][0];
11020 int yy = y + xy[i][1];
11021 int center_side = trigger_sides[i][0];
11022 int border_side = trigger_sides[i][1];
11023 int border_element;
11025 if (!IN_LEV_FIELD(xx, yy))
11028 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11029 border_element = Feld[xx][yy]; /* may be moving! */
11030 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11031 border_element = Feld[xx][yy];
11032 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11033 border_element = MovingOrBlocked2Element(xx, yy);
11035 continue; /* center and border element do not touch */
11037 /* check for change of center element (but change it only once) */
11038 if (IS_CUSTOM_ELEMENT(center_element) &&
11039 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
11040 !change_center_element)
11042 for (j = 0; j < element_info[center_element].num_change_pages; j++)
11044 struct ElementChangeInfo *change =
11045 &element_info[center_element].change_page[j];
11047 if (change->can_change &&
11048 change->has_event[CE_TOUCHING_X] &&
11049 change->trigger_side & border_side &&
11051 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
11053 change->trigger_element == border_element
11057 change_center_element = TRUE;
11058 center_element_change_page = j;
11059 border_trigger_element = border_element;
11066 /* check for change of border element */
11067 if (IS_CUSTOM_ELEMENT(border_element) &&
11068 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
11070 for (j = 0; j < element_info[border_element].num_change_pages; j++)
11072 struct ElementChangeInfo *change =
11073 &element_info[border_element].change_page[j];
11075 if (change->can_change &&
11076 change->has_event[CE_TOUCHING_X] &&
11077 change->trigger_side & center_side &&
11079 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
11081 change->trigger_element == center_element
11086 printf("::: border_element %d, %d\n", x, y);
11089 CheckElementChangeByPage(xx, yy, border_element, center_element,
11097 if (change_center_element)
11100 printf("::: center_element %d, %d\n", x, y);
11103 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
11104 CE_TOUCHING_X, center_element_change_page);
11108 void TestIfElementHitsCustomElement(int x, int y, int direction)
11110 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11111 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11112 int hitx = x + dx, hity = y + dy;
11113 int hitting_element = Feld[x][y];
11114 int touched_element;
11116 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11117 !IS_FREE(hitx, hity) &&
11118 (!IS_MOVING(hitx, hity) ||
11119 MovDir[hitx][hity] != direction ||
11120 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11123 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11127 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11131 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11132 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11134 #if !USE_HITTING_SOMETHING_BUGFIX
11135 /* "hitting something" is also true when hitting the playfield border */
11136 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11137 CE_HITTING_SOMETHING, direction);
11140 if (IN_LEV_FIELD(hitx, hity))
11142 int opposite_direction = MV_DIR_OPPOSITE(direction);
11143 int hitting_side = direction;
11144 int touched_side = opposite_direction;
11146 int touched_element = MovingOrBlocked2Element(hitx, hity);
11149 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11150 MovDir[hitx][hity] != direction ||
11151 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11160 #if !USE_HIT_BY_SOMETHING_BUGFIX
11161 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11162 CE_HIT_BY_SOMETHING, opposite_direction);
11165 if (IS_CUSTOM_ELEMENT(hitting_element) &&
11166 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
11168 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
11170 struct ElementChangeInfo *change =
11171 &element_info[hitting_element].change_page[i];
11173 if (change->can_change &&
11174 change->has_event[CE_HITTING_X] &&
11175 change->trigger_side & touched_side &&
11178 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
11180 change->trigger_element == touched_element
11184 CheckElementChangeByPage(x, y, hitting_element, touched_element,
11191 if (IS_CUSTOM_ELEMENT(touched_element) &&
11192 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
11194 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11196 struct ElementChangeInfo *change =
11197 &element_info[touched_element].change_page[i];
11199 if (change->can_change &&
11200 change->has_event[CE_HIT_BY_X] &&
11201 change->trigger_side & hitting_side &&
11203 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11205 change->trigger_element == hitting_element
11209 CheckElementChangeByPage(hitx, hity, touched_element,
11210 hitting_element, CE_HIT_BY_X, i);
11216 #if USE_HIT_BY_SOMETHING_BUGFIX
11217 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11218 CE_HIT_BY_SOMETHING, opposite_direction);
11223 #if USE_HITTING_SOMETHING_BUGFIX
11224 /* "hitting something" is also true when hitting the playfield border */
11225 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11226 CE_HITTING_SOMETHING, direction);
11231 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11233 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11234 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11235 int hitx = x + dx, hity = y + dy;
11236 int hitting_element = Feld[x][y];
11237 int touched_element;
11239 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11240 !IS_FREE(hitx, hity) &&
11241 (!IS_MOVING(hitx, hity) ||
11242 MovDir[hitx][hity] != direction ||
11243 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11246 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11250 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11254 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11255 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11257 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11258 EP_CAN_SMASH_EVERYTHING, direction);
11260 if (IN_LEV_FIELD(hitx, hity))
11262 int opposite_direction = MV_DIR_OPPOSITE(direction);
11263 int hitting_side = direction;
11264 int touched_side = opposite_direction;
11266 int touched_element = MovingOrBlocked2Element(hitx, hity);
11269 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11270 MovDir[hitx][hity] != direction ||
11271 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11280 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11281 CE_SMASHED_BY_SOMETHING, opposite_direction);
11283 if (IS_CUSTOM_ELEMENT(hitting_element) &&
11284 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
11286 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
11288 struct ElementChangeInfo *change =
11289 &element_info[hitting_element].change_page[i];
11291 if (change->can_change &&
11292 change->has_event[CE_OTHER_IS_SMASHING] &&
11293 change->trigger_side & touched_side &&
11296 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
11298 change->trigger_element == touched_element
11302 CheckElementChangeByPage(x, y, hitting_element, touched_element,
11303 CE_OTHER_IS_SMASHING, i);
11309 if (IS_CUSTOM_ELEMENT(touched_element) &&
11310 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
11312 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11314 struct ElementChangeInfo *change =
11315 &element_info[touched_element].change_page[i];
11317 if (change->can_change &&
11318 change->has_event[CE_OTHER_GETS_SMASHED] &&
11319 change->trigger_side & hitting_side &&
11321 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11323 change->trigger_element == hitting_element
11327 CheckElementChangeByPage(hitx, hity, touched_element,
11328 hitting_element, CE_OTHER_GETS_SMASHED,i);
11338 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11340 int i, kill_x = -1, kill_y = -1;
11341 int bad_element = -1;
11342 static int test_xy[4][2] =
11349 static int test_dir[4] =
11357 for (i = 0; i < NUM_DIRECTIONS; i++)
11359 int test_x, test_y, test_move_dir, test_element;
11361 test_x = good_x + test_xy[i][0];
11362 test_y = good_y + test_xy[i][1];
11364 if (!IN_LEV_FIELD(test_x, test_y))
11368 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11371 test_element = Feld[test_x][test_y];
11373 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11376 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11377 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11379 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11380 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11384 bad_element = test_element;
11390 if (kill_x != -1 || kill_y != -1)
11392 if (IS_PLAYER(good_x, good_y))
11394 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11397 if (player->shield_deadly_time_left > 0 &&
11398 !IS_INDESTRUCTIBLE(bad_element))
11399 Bang(kill_x, kill_y);
11400 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11403 if (player->shield_deadly_time_left > 0)
11404 Bang(kill_x, kill_y);
11405 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11410 Bang(good_x, good_y);
11414 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11416 int i, kill_x = -1, kill_y = -1;
11417 int bad_element = Feld[bad_x][bad_y];
11418 static int test_xy[4][2] =
11425 static int touch_dir[4] =
11427 MV_LEFT | MV_RIGHT,
11432 static int test_dir[4] =
11440 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11443 for (i = 0; i < NUM_DIRECTIONS; i++)
11445 int test_x, test_y, test_move_dir, test_element;
11447 test_x = bad_x + test_xy[i][0];
11448 test_y = bad_y + test_xy[i][1];
11449 if (!IN_LEV_FIELD(test_x, test_y))
11453 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11455 test_element = Feld[test_x][test_y];
11457 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11458 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11460 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11461 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11463 /* good thing is player or penguin that does not move away */
11464 if (IS_PLAYER(test_x, test_y))
11466 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11468 if (bad_element == EL_ROBOT && player->is_moving)
11469 continue; /* robot does not kill player if he is moving */
11471 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11473 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11474 continue; /* center and border element do not touch */
11481 else if (test_element == EL_PENGUIN)
11490 if (kill_x != -1 || kill_y != -1)
11492 if (IS_PLAYER(kill_x, kill_y))
11494 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11497 if (player->shield_deadly_time_left > 0 &&
11498 !IS_INDESTRUCTIBLE(bad_element))
11499 Bang(bad_x, bad_y);
11500 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11503 if (player->shield_deadly_time_left > 0)
11504 Bang(bad_x, bad_y);
11505 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11510 Bang(kill_x, kill_y);
11514 void TestIfHeroTouchesBadThing(int x, int y)
11516 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11519 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11521 TestIfGoodThingHitsBadThing(x, y, move_dir);
11524 void TestIfBadThingTouchesHero(int x, int y)
11526 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11529 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11531 TestIfBadThingHitsGoodThing(x, y, move_dir);
11534 void TestIfFriendTouchesBadThing(int x, int y)
11536 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11539 void TestIfBadThingTouchesFriend(int x, int y)
11541 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11544 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11546 int i, kill_x = bad_x, kill_y = bad_y;
11547 static int xy[4][2] =
11555 for (i = 0; i < NUM_DIRECTIONS; i++)
11559 x = bad_x + xy[i][0];
11560 y = bad_y + xy[i][1];
11561 if (!IN_LEV_FIELD(x, y))
11564 element = Feld[x][y];
11565 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11566 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11574 if (kill_x != bad_x || kill_y != bad_y)
11575 Bang(bad_x, bad_y);
11578 void KillHero(struct PlayerInfo *player)
11580 int jx = player->jx, jy = player->jy;
11582 if (!player->active)
11585 /* remove accessible field at the player's position */
11586 Feld[jx][jy] = EL_EMPTY;
11588 /* deactivate shield (else Bang()/Explode() would not work right) */
11589 player->shield_normal_time_left = 0;
11590 player->shield_deadly_time_left = 0;
11596 static void KillHeroUnlessEnemyProtected(int x, int y)
11598 if (!PLAYER_ENEMY_PROTECTED(x, y))
11599 KillHero(PLAYERINFO(x, y));
11602 static void KillHeroUnlessExplosionProtected(int x, int y)
11604 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11605 KillHero(PLAYERINFO(x, y));
11608 void BuryHero(struct PlayerInfo *player)
11610 int jx = player->jx, jy = player->jy;
11612 if (!player->active)
11616 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11618 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11620 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11622 player->GameOver = TRUE;
11623 RemoveHero(player);
11626 void RemoveHero(struct PlayerInfo *player)
11628 int jx = player->jx, jy = player->jy;
11629 int i, found = FALSE;
11631 player->present = FALSE;
11632 player->active = FALSE;
11634 if (!ExplodeField[jx][jy])
11635 StorePlayer[jx][jy] = 0;
11637 for (i = 0; i < MAX_PLAYERS; i++)
11638 if (stored_player[i].active)
11642 AllPlayersGone = TRUE;
11649 =============================================================================
11650 checkDiagonalPushing()
11651 -----------------------------------------------------------------------------
11652 check if diagonal input device direction results in pushing of object
11653 (by checking if the alternative direction is walkable, diggable, ...)
11654 =============================================================================
11657 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11658 int x, int y, int real_dx, int real_dy)
11660 int jx, jy, dx, dy, xx, yy;
11662 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11665 /* diagonal direction: check alternative direction */
11670 xx = jx + (dx == 0 ? real_dx : 0);
11671 yy = jy + (dy == 0 ? real_dy : 0);
11673 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11677 =============================================================================
11679 -----------------------------------------------------------------------------
11680 x, y: field next to player (non-diagonal) to try to dig to
11681 real_dx, real_dy: direction as read from input device (can be diagonal)
11682 =============================================================================
11685 int DigField(struct PlayerInfo *player,
11686 int oldx, int oldy, int x, int y,
11687 int real_dx, int real_dy, int mode)
11690 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11692 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11693 boolean player_was_pushing = player->is_pushing;
11694 int jx = oldx, jy = oldy;
11695 int dx = x - jx, dy = y - jy;
11696 int nextx = x + dx, nexty = y + dy;
11697 int move_direction = (dx == -1 ? MV_LEFT :
11698 dx == +1 ? MV_RIGHT :
11700 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11701 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11703 int dig_side = MV_DIR_OPPOSITE(move_direction);
11705 static int trigger_sides[4] =
11707 CH_SIDE_RIGHT, /* moving left */
11708 CH_SIDE_LEFT, /* moving right */
11709 CH_SIDE_BOTTOM, /* moving up */
11710 CH_SIDE_TOP, /* moving down */
11712 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11714 int old_element = Feld[jx][jy];
11717 if (is_player) /* function can also be called by EL_PENGUIN */
11719 if (player->MovPos == 0)
11721 player->is_digging = FALSE;
11722 player->is_collecting = FALSE;
11725 if (player->MovPos == 0) /* last pushing move finished */
11726 player->is_pushing = FALSE;
11728 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11730 player->is_switching = FALSE;
11731 #if USE_NEW_PUSH_DELAY
11732 player->push_delay = -1;
11734 player->push_delay = 0;
11737 return MF_NO_ACTION;
11741 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11742 return MF_NO_ACTION;
11747 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11749 if (IS_TUBE(Feld[jx][jy]) ||
11750 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11754 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11755 int tube_leave_directions[][2] =
11757 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11758 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11759 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11760 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11761 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11762 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11763 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11764 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11765 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11766 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11767 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11768 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11771 while (tube_leave_directions[i][0] != tube_element)
11774 if (tube_leave_directions[i][0] == -1) /* should not happen */
11778 if (!(tube_leave_directions[i][1] & move_direction))
11779 return MF_NO_ACTION; /* tube has no opening in this direction */
11784 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11785 old_element = Back[jx][jy];
11786 #if USE_BACK_WALKABLE_BUGFIX
11788 /* in case of element dropped at player position, check background */
11789 else if (Back[jx][jy] != EL_EMPTY &&
11790 game.engine_version >= VERSION_IDENT(2,2,0,0))
11791 old_element = Back[jx][jy];
11796 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11797 return MF_NO_ACTION; /* field has no opening in this direction */
11799 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11800 return MF_NO_ACTION; /* field has no opening in this direction */
11802 element = Feld[x][y];
11804 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11805 return MF_NO_ACTION;
11807 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11808 game.engine_version >= VERSION_IDENT(2,2,0,0))
11809 return MF_NO_ACTION;
11812 if (game.gravity && is_player && !player->is_auto_moving &&
11813 canFallDown(player) && move_direction != MV_DOWN &&
11814 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11815 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11819 if (element == EL_EMPTY_SPACE &&
11820 game.gravity && !player->is_auto_moving &&
11821 canFallDown(player) && move_direction != MV_DOWN)
11822 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11828 case EL_SP_PORT_LEFT:
11829 case EL_SP_PORT_RIGHT:
11830 case EL_SP_PORT_UP:
11831 case EL_SP_PORT_DOWN:
11832 case EL_SP_PORT_HORIZONTAL:
11833 case EL_SP_PORT_VERTICAL:
11834 case EL_SP_PORT_ANY:
11835 case EL_SP_GRAVITY_PORT_LEFT:
11836 case EL_SP_GRAVITY_PORT_RIGHT:
11837 case EL_SP_GRAVITY_PORT_UP:
11838 case EL_SP_GRAVITY_PORT_DOWN:
11840 if (!canEnterSupaplexPort(x, y, dx, dy))
11841 return MF_NO_ACTION;
11844 element != EL_SP_PORT_LEFT &&
11845 element != EL_SP_GRAVITY_PORT_LEFT &&
11846 element != EL_SP_PORT_HORIZONTAL &&
11847 element != EL_SP_PORT_ANY) ||
11849 element != EL_SP_PORT_RIGHT &&
11850 element != EL_SP_GRAVITY_PORT_RIGHT &&
11851 element != EL_SP_PORT_HORIZONTAL &&
11852 element != EL_SP_PORT_ANY) ||
11854 element != EL_SP_PORT_UP &&
11855 element != EL_SP_GRAVITY_PORT_UP &&
11856 element != EL_SP_PORT_VERTICAL &&
11857 element != EL_SP_PORT_ANY) ||
11859 element != EL_SP_PORT_DOWN &&
11860 element != EL_SP_GRAVITY_PORT_DOWN &&
11861 element != EL_SP_PORT_VERTICAL &&
11862 element != EL_SP_PORT_ANY) ||
11863 !IN_LEV_FIELD(nextx, nexty) ||
11864 !IS_FREE(nextx, nexty))
11865 return MF_NO_ACTION;
11868 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11869 element == EL_SP_GRAVITY_PORT_RIGHT ||
11870 element == EL_SP_GRAVITY_PORT_UP ||
11871 element == EL_SP_GRAVITY_PORT_DOWN)
11872 game.gravity = !game.gravity;
11874 /* automatically move to the next field with double speed */
11875 player->programmed_action = move_direction;
11877 if (player->move_delay_reset_counter == 0)
11879 player->move_delay_reset_counter = 2; /* two double speed steps */
11881 DOUBLE_PLAYER_SPEED(player);
11884 player->move_delay_reset_counter = 2;
11886 DOUBLE_PLAYER_SPEED(player);
11890 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11893 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11899 case EL_TUBE_VERTICAL:
11900 case EL_TUBE_HORIZONTAL:
11901 case EL_TUBE_VERTICAL_LEFT:
11902 case EL_TUBE_VERTICAL_RIGHT:
11903 case EL_TUBE_HORIZONTAL_UP:
11904 case EL_TUBE_HORIZONTAL_DOWN:
11905 case EL_TUBE_LEFT_UP:
11906 case EL_TUBE_LEFT_DOWN:
11907 case EL_TUBE_RIGHT_UP:
11908 case EL_TUBE_RIGHT_DOWN:
11911 int tube_enter_directions[][2] =
11913 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11914 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11915 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11916 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11917 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11918 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11919 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11920 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11921 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11922 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11923 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11924 { -1, MV_NO_MOVING }
11927 while (tube_enter_directions[i][0] != element)
11930 if (tube_enter_directions[i][0] == -1) /* should not happen */
11934 if (!(tube_enter_directions[i][1] & move_direction))
11935 return MF_NO_ACTION; /* tube has no opening in this direction */
11937 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11945 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11947 if (IS_WALKABLE(element))
11950 int sound_element = SND_ELEMENT(element);
11951 int sound_action = ACTION_WALKING;
11954 if (!ACCESS_FROM(element, opposite_direction))
11955 return MF_NO_ACTION; /* field not accessible from this direction */
11959 if (element == EL_EMPTY_SPACE &&
11960 game.gravity && !player->is_auto_moving &&
11961 canFallDown(player) && move_direction != MV_DOWN)
11962 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11965 if (IS_RND_GATE(element))
11967 if (!player->key[RND_GATE_NR(element)])
11968 return MF_NO_ACTION;
11970 else if (IS_RND_GATE_GRAY(element))
11972 if (!player->key[RND_GATE_GRAY_NR(element)])
11973 return MF_NO_ACTION;
11975 else if (element == EL_EXIT_OPEN ||
11976 element == EL_SP_EXIT_OPEN ||
11977 element == EL_SP_EXIT_OPENING)
11979 sound_action = ACTION_PASSING; /* player is passing exit */
11981 else if (element == EL_EMPTY)
11983 sound_action = ACTION_MOVING; /* nothing to walk on */
11986 /* play sound from background or player, whatever is available */
11987 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11988 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11990 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11995 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11997 else if (IS_PASSABLE(element))
12001 if (!canPassField(x, y, move_direction))
12002 return MF_NO_ACTION;
12007 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
12008 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
12009 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
12010 return MF_NO_ACTION;
12012 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
12013 return MF_NO_ACTION;
12018 if (!ACCESS_FROM(element, opposite_direction))
12019 return MF_NO_ACTION; /* field not accessible from this direction */
12021 if (IS_CUSTOM_ELEMENT(element) &&
12022 !ACCESS_FROM(element, opposite_direction))
12023 return MF_NO_ACTION; /* field not accessible from this direction */
12027 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12028 return MF_NO_ACTION;
12033 if (IS_EM_GATE(element))
12035 if (!player->key[EM_GATE_NR(element)])
12036 return MF_NO_ACTION;
12038 else if (IS_EM_GATE_GRAY(element))
12040 if (!player->key[EM_GATE_GRAY_NR(element)])
12041 return MF_NO_ACTION;
12043 else if (IS_SP_PORT(element))
12045 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12046 element == EL_SP_GRAVITY_PORT_RIGHT ||
12047 element == EL_SP_GRAVITY_PORT_UP ||
12048 element == EL_SP_GRAVITY_PORT_DOWN)
12049 game.gravity = !game.gravity;
12050 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12051 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12052 element == EL_SP_GRAVITY_ON_PORT_UP ||
12053 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12054 game.gravity = TRUE;
12055 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12056 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12057 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12058 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12059 game.gravity = FALSE;
12062 /* automatically move to the next field with double speed */
12063 player->programmed_action = move_direction;
12065 if (player->move_delay_reset_counter == 0)
12067 player->move_delay_reset_counter = 2; /* two double speed steps */
12069 DOUBLE_PLAYER_SPEED(player);
12072 player->move_delay_reset_counter = 2;
12074 DOUBLE_PLAYER_SPEED(player);
12077 PlayLevelSoundAction(x, y, ACTION_PASSING);
12081 else if (IS_DIGGABLE(element))
12085 if (mode != DF_SNAP)
12088 GfxElement[x][y] = GFX_ELEMENT(element);
12091 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
12093 player->is_digging = TRUE;
12096 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12098 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12099 player->index_bit, dig_side);
12102 if (mode == DF_SNAP)
12103 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12108 else if (IS_COLLECTIBLE(element))
12112 if (is_player && mode != DF_SNAP)
12114 GfxElement[x][y] = element;
12115 player->is_collecting = TRUE;
12118 if (element == EL_SPEED_PILL)
12120 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12122 else if (element == EL_EXTRA_TIME && level.time > 0)
12125 DrawGameValue_Time(TimeLeft);
12127 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12129 player->shield_normal_time_left += 10;
12130 if (element == EL_SHIELD_DEADLY)
12131 player->shield_deadly_time_left += 10;
12133 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
12135 if (player->inventory_size < MAX_INVENTORY_SIZE)
12136 player->inventory_element[player->inventory_size++] = element;
12138 DrawGameValue_Dynamite(local_player->inventory_size);
12140 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12142 player->dynabomb_count++;
12143 player->dynabombs_left++;
12145 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12147 player->dynabomb_size++;
12149 else if (element == EL_DYNABOMB_INCREASE_POWER)
12151 player->dynabomb_xl = TRUE;
12153 else if (IS_KEY(element))
12155 player->key[KEY_NR(element)] = TRUE;
12157 DrawGameValue_Keys(player->key);
12159 redraw_mask |= REDRAW_DOOR_1;
12161 else if (IS_ENVELOPE(element))
12164 player->show_envelope = element;
12166 ShowEnvelope(element - EL_ENVELOPE_1);
12169 else if (IS_DROPPABLE(element) ||
12170 IS_THROWABLE(element)) /* can be collected and dropped */
12174 if (element_info[element].collect_count == 0)
12175 player->inventory_infinite_element = element;
12177 for (i = 0; i < element_info[element].collect_count; i++)
12178 if (player->inventory_size < MAX_INVENTORY_SIZE)
12179 player->inventory_element[player->inventory_size++] = element;
12181 DrawGameValue_Dynamite(local_player->inventory_size);
12183 else if (element_info[element].collect_count > 0)
12185 local_player->gems_still_needed -=
12186 element_info[element].collect_count;
12187 if (local_player->gems_still_needed < 0)
12188 local_player->gems_still_needed = 0;
12190 DrawGameValue_Emeralds(local_player->gems_still_needed);
12193 RaiseScoreElement(element);
12194 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12197 CheckTriggeredElementChangeByPlayer(x, y, element,
12198 CE_PLAYER_COLLECTS_X,
12199 player->index_bit, dig_side);
12202 if (mode == DF_SNAP)
12203 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12208 else if (IS_PUSHABLE(element))
12210 if (mode == DF_SNAP && element != EL_BD_ROCK)
12211 return MF_NO_ACTION;
12213 if (CAN_FALL(element) && dy)
12214 return MF_NO_ACTION;
12216 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12217 !(element == EL_SPRING && level.use_spring_bug))
12218 return MF_NO_ACTION;
12221 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12222 ((move_direction & MV_VERTICAL &&
12223 ((element_info[element].move_pattern & MV_LEFT &&
12224 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12225 (element_info[element].move_pattern & MV_RIGHT &&
12226 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12227 (move_direction & MV_HORIZONTAL &&
12228 ((element_info[element].move_pattern & MV_UP &&
12229 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12230 (element_info[element].move_pattern & MV_DOWN &&
12231 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12232 return MF_NO_ACTION;
12236 /* do not push elements already moving away faster than player */
12237 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12238 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12239 return MF_NO_ACTION;
12241 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
12242 return MF_NO_ACTION;
12248 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12250 if (player->push_delay_value == -1 || !player_was_pushing)
12251 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12253 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12255 if (player->push_delay_value == -1)
12256 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12259 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12261 if (player->push_delay_value == -1 || !player_was_pushing)
12262 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12265 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12267 if (!player->is_pushing)
12268 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12272 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
12273 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
12274 !player_is_pushing))
12275 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12278 if (!player->is_pushing &&
12279 game.engine_version >= VERSION_IDENT(2,2,0,7))
12280 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12284 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
12285 player->push_delay, player->push_delay_value,
12286 FrameCounter, game.engine_version,
12287 player_was_pushing, player->is_pushing,
12288 element, element_info[element].token_name,
12289 GET_NEW_PUSH_DELAY(element));
12292 player->is_pushing = TRUE;
12294 if (!(IN_LEV_FIELD(nextx, nexty) &&
12295 (IS_FREE(nextx, nexty) ||
12296 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12297 IS_SB_ELEMENT(element)))))
12298 return MF_NO_ACTION;
12300 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12301 return MF_NO_ACTION;
12303 #if USE_NEW_PUSH_DELAY
12306 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
12307 printf("::: ALERT: %d, %d [%d / %d]\n",
12308 player->push_delay, player->push_delay2,
12309 FrameCounter, FrameCounter / 50);
12312 if (player->push_delay == -1) /* new pushing; restart delay */
12313 player->push_delay = 0;
12315 if (player->push_delay == 0) /* new pushing; restart delay */
12316 player->push_delay = FrameCounter;
12319 #if USE_NEW_PUSH_DELAY
12321 if ( (player->push_delay > 0) != (!xxx_fr) )
12322 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
12323 player->push_delay,
12324 xxx_pdv2, player->push_delay2, player->push_delay_value,
12325 FrameCounter, FrameCounter / 50);
12329 if (player->push_delay > 0 &&
12330 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12331 element != EL_SPRING && element != EL_BALLOON)
12334 if (player->push_delay < player->push_delay_value &&
12335 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12336 element != EL_SPRING && element != EL_BALLOON)
12340 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12341 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12342 element != EL_SPRING && element != EL_BALLOON)
12345 /* make sure that there is no move delay before next try to push */
12346 #if USE_NEW_MOVE_DELAY
12347 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12348 player->move_delay = 0;
12350 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12351 player->move_delay = INITIAL_MOVE_DELAY_OFF;
12354 return MF_NO_ACTION;
12358 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12361 if (IS_SB_ELEMENT(element))
12363 if (element == EL_SOKOBAN_FIELD_FULL)
12365 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12366 local_player->sokobanfields_still_needed++;
12369 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12371 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12372 local_player->sokobanfields_still_needed--;
12375 Feld[x][y] = EL_SOKOBAN_OBJECT;
12377 if (Back[x][y] == Back[nextx][nexty])
12378 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12379 else if (Back[x][y] != 0)
12380 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12383 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12386 if (local_player->sokobanfields_still_needed == 0 &&
12387 game.emulation == EMU_SOKOBAN)
12389 player->LevelSolved = player->GameOver = TRUE;
12390 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12394 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12396 InitMovingField(x, y, move_direction);
12397 GfxAction[x][y] = ACTION_PUSHING;
12399 if (mode == DF_SNAP)
12400 ContinueMoving(x, y);
12402 MovPos[x][y] = (dx != 0 ? dx : dy);
12404 Pushed[x][y] = TRUE;
12405 Pushed[nextx][nexty] = TRUE;
12407 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12408 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12410 player->push_delay_value = -1; /* get new value later */
12412 #if USE_PUSH_BUGFIX
12413 /* now: check for element change _after_ element has been pushed! */
12415 if (game.use_change_when_pushing_bug)
12417 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12420 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12421 player->index_bit, dig_side);
12422 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12423 player->index_bit, dig_side);
12429 /* check for element change _after_ element has been pushed! */
12433 /* !!! TEST ONLY !!! */
12434 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12435 player->index_bit, dig_side);
12436 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12437 player->index_bit, dig_side);
12439 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12440 player->index_bit, dig_side);
12441 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12442 player->index_bit, dig_side);
12450 else if (IS_SWITCHABLE(element))
12452 if (PLAYER_SWITCHING(player, x, y))
12454 CheckTriggeredElementChangeByPlayer(x,y, element,
12455 CE_PLAYER_PRESSES_X,
12456 player->index_bit, dig_side);
12461 player->is_switching = TRUE;
12462 player->switch_x = x;
12463 player->switch_y = y;
12465 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12467 if (element == EL_ROBOT_WHEEL)
12469 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12473 DrawLevelField(x, y);
12475 else if (element == EL_SP_TERMINAL)
12479 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12481 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12483 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12484 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12487 else if (IS_BELT_SWITCH(element))
12489 ToggleBeltSwitch(x, y);
12491 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12492 element == EL_SWITCHGATE_SWITCH_DOWN)
12494 ToggleSwitchgateSwitch(x, y);
12496 else if (element == EL_LIGHT_SWITCH ||
12497 element == EL_LIGHT_SWITCH_ACTIVE)
12499 ToggleLightSwitch(x, y);
12502 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12503 SND_LIGHT_SWITCH_ACTIVATING :
12504 SND_LIGHT_SWITCH_DEACTIVATING);
12507 else if (element == EL_TIMEGATE_SWITCH)
12509 ActivateTimegateSwitch(x, y);
12511 else if (element == EL_BALLOON_SWITCH_LEFT ||
12512 element == EL_BALLOON_SWITCH_RIGHT ||
12513 element == EL_BALLOON_SWITCH_UP ||
12514 element == EL_BALLOON_SWITCH_DOWN ||
12515 element == EL_BALLOON_SWITCH_ANY)
12517 if (element == EL_BALLOON_SWITCH_ANY)
12518 game.balloon_dir = move_direction;
12520 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12521 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12522 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12523 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12526 else if (element == EL_LAMP)
12528 Feld[x][y] = EL_LAMP_ACTIVE;
12529 local_player->lights_still_needed--;
12531 ResetGfxAnimation(x, y);
12532 DrawLevelField(x, y);
12534 else if (element == EL_TIME_ORB_FULL)
12536 Feld[x][y] = EL_TIME_ORB_EMPTY;
12538 DrawGameValue_Time(TimeLeft);
12540 ResetGfxAnimation(x, y);
12541 DrawLevelField(x, y);
12544 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12548 CheckTriggeredElementChangeByPlayer(x, y, element,
12550 player->index_bit, dig_side);
12552 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12553 player->index_bit, dig_side);
12559 if (!PLAYER_SWITCHING(player, x, y))
12561 player->is_switching = TRUE;
12562 player->switch_x = x;
12563 player->switch_y = y;
12566 /* !!! TEST ONLY !!! */
12567 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12568 player->index_bit, dig_side);
12569 CheckTriggeredElementChangeByPlayer(x, y, element,
12571 player->index_bit, dig_side);
12573 CheckTriggeredElementChangeByPlayer(x, y, element,
12575 player->index_bit, dig_side);
12576 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12577 player->index_bit, dig_side);
12582 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12583 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12584 player->index_bit, dig_side);
12585 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12586 player->index_bit, dig_side);
12588 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12589 player->index_bit, dig_side);
12590 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12591 player->index_bit, dig_side);
12595 return MF_NO_ACTION;
12598 #if USE_NEW_PUSH_DELAY
12599 player->push_delay = -1;
12601 player->push_delay = 0;
12604 #if USE_PENGUIN_COLLECT_BUGFIX
12605 if (is_player) /* function can also be called by EL_PENGUIN */
12608 if (Feld[x][y] != element) /* really digged/collected something */
12609 player->is_collecting = !player->is_digging;
12615 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12617 int jx = player->jx, jy = player->jy;
12618 int x = jx + dx, y = jy + dy;
12619 int snap_direction = (dx == -1 ? MV_LEFT :
12620 dx == +1 ? MV_RIGHT :
12622 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12625 if (player->MovPos != 0)
12628 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12632 if (!player->active || !IN_LEV_FIELD(x, y))
12640 if (player->MovPos == 0)
12641 player->is_pushing = FALSE;
12643 player->is_snapping = FALSE;
12645 if (player->MovPos == 0)
12647 player->is_moving = FALSE;
12648 player->is_digging = FALSE;
12649 player->is_collecting = FALSE;
12655 if (player->is_snapping)
12658 player->MovDir = snap_direction;
12661 if (player->MovPos == 0)
12664 player->is_moving = FALSE;
12665 player->is_digging = FALSE;
12666 player->is_collecting = FALSE;
12669 player->is_dropping = FALSE;
12671 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12674 player->is_snapping = TRUE;
12677 if (player->MovPos == 0)
12680 player->is_moving = FALSE;
12681 player->is_digging = FALSE;
12682 player->is_collecting = FALSE;
12686 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12687 DrawLevelField(player->last_jx, player->last_jy);
12690 DrawLevelField(x, y);
12699 boolean DropElement(struct PlayerInfo *player)
12701 int old_element, new_element;
12702 int dropx = player->jx, dropy = player->jy;
12703 int drop_direction = player->MovDir;
12705 int drop_side = drop_direction;
12707 static int trigger_sides[4] =
12709 CH_SIDE_LEFT, /* dropping left */
12710 CH_SIDE_RIGHT, /* dropping right */
12711 CH_SIDE_TOP, /* dropping up */
12712 CH_SIDE_BOTTOM, /* dropping down */
12714 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12716 int drop_element = (player->inventory_size > 0 ?
12717 player->inventory_element[player->inventory_size - 1] :
12718 player->inventory_infinite_element != EL_UNDEFINED ?
12719 player->inventory_infinite_element :
12720 player->dynabombs_left > 0 ?
12721 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12724 #if USE_DROP_BUGFIX
12725 /* do not drop an element on top of another element; when holding drop key
12726 pressed without moving, dropped element must move away before the next
12727 element can be dropped (this is especially important if the next element
12728 is dynamite, which can be placed on background for historical reasons) */
12729 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12733 if (IS_THROWABLE(drop_element))
12735 dropx += GET_DX_FROM_DIR(drop_direction);
12736 dropy += GET_DY_FROM_DIR(drop_direction);
12738 if (!IN_LEV_FIELD(dropx, dropy))
12742 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12743 new_element = drop_element; /* default: no change when dropping */
12745 /* check if player is active, not moving and ready to drop */
12746 if (!player->active || player->MovPos || player->drop_delay > 0)
12749 /* check if player has anything that can be dropped */
12751 if (new_element == EL_UNDEFINED)
12754 if (player->inventory_size == 0 &&
12755 player->inventory_infinite_element == EL_UNDEFINED &&
12756 player->dynabombs_left == 0)
12760 /* check if anything can be dropped at the current position */
12761 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12764 /* collected custom elements can only be dropped on empty fields */
12766 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12769 if (player->inventory_size > 0 &&
12770 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12771 && old_element != EL_EMPTY)
12775 if (old_element != EL_EMPTY)
12776 Back[dropx][dropy] = old_element; /* store old element on this field */
12778 ResetGfxAnimation(dropx, dropy);
12779 ResetRandomAnimationValue(dropx, dropy);
12781 if (player->inventory_size > 0 ||
12782 player->inventory_infinite_element != EL_UNDEFINED)
12784 if (player->inventory_size > 0)
12786 player->inventory_size--;
12789 new_element = player->inventory_element[player->inventory_size];
12792 DrawGameValue_Dynamite(local_player->inventory_size);
12794 if (new_element == EL_DYNAMITE)
12795 new_element = EL_DYNAMITE_ACTIVE;
12796 else if (new_element == EL_SP_DISK_RED)
12797 new_element = EL_SP_DISK_RED_ACTIVE;
12800 Feld[dropx][dropy] = new_element;
12802 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12803 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12804 el2img(Feld[dropx][dropy]), 0);
12806 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12809 /* needed if previous element just changed to "empty" in the last frame */
12810 Changed[dropx][dropy] = FALSE; /* allow another change */
12814 /* !!! TEST ONLY !!! */
12815 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12816 player->index_bit, drop_side);
12817 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12819 player->index_bit, drop_side);
12821 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12823 player->index_bit, drop_side);
12824 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12825 player->index_bit, drop_side);
12828 TestIfElementTouchesCustomElement(dropx, dropy);
12830 else /* player is dropping a dyna bomb */
12832 player->dynabombs_left--;
12835 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12838 Feld[dropx][dropy] = new_element;
12840 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12841 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12842 el2img(Feld[dropx][dropy]), 0);
12844 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12851 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12854 InitField_WithBug1(dropx, dropy, FALSE);
12856 InitField(dropx, dropy, FALSE);
12857 if (CAN_MOVE(Feld[dropx][dropy]))
12858 InitMovDir(dropx, dropy);
12862 new_element = Feld[dropx][dropy]; /* element might have changed */
12864 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12865 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12868 int move_stepsize = element_info[new_element].move_stepsize;
12870 int move_direction, nextx, nexty;
12872 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12873 MovDir[dropx][dropy] = drop_direction;
12875 move_direction = MovDir[dropx][dropy];
12876 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12877 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12880 Changed[dropx][dropy] = FALSE; /* allow another change */
12881 CheckCollision[dropx][dropy] = 2;
12884 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12887 WasJustMoving[dropx][dropy] = 3;
12890 InitMovingField(dropx, dropy, move_direction);
12891 ContinueMoving(dropx, dropy);
12896 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12899 Changed[dropx][dropy] = FALSE; /* allow another change */
12902 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12904 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12905 CE_HITTING_SOMETHING, move_direction);
12913 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12918 player->drop_delay = 8 + 8 + 8;
12922 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12927 player->is_dropping = TRUE;
12929 #if USE_DROP_BUGFIX
12930 player->drop_x = dropx;
12931 player->drop_y = dropy;
12937 /* ------------------------------------------------------------------------- */
12938 /* game sound playing functions */
12939 /* ------------------------------------------------------------------------- */
12941 static int *loop_sound_frame = NULL;
12942 static int *loop_sound_volume = NULL;
12944 void InitPlayLevelSound()
12946 int num_sounds = getSoundListSize();
12948 checked_free(loop_sound_frame);
12949 checked_free(loop_sound_volume);
12951 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12952 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12955 static void PlayLevelSound(int x, int y, int nr)
12957 int sx = SCREENX(x), sy = SCREENY(y);
12958 int volume, stereo_position;
12959 int max_distance = 8;
12960 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12962 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12963 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12966 if (!IN_LEV_FIELD(x, y) ||
12967 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12968 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12971 volume = SOUND_MAX_VOLUME;
12973 if (!IN_SCR_FIELD(sx, sy))
12975 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12976 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12978 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12981 stereo_position = (SOUND_MAX_LEFT +
12982 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12983 (SCR_FIELDX + 2 * max_distance));
12985 if (IS_LOOP_SOUND(nr))
12987 /* This assures that quieter loop sounds do not overwrite louder ones,
12988 while restarting sound volume comparison with each new game frame. */
12990 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12993 loop_sound_volume[nr] = volume;
12994 loop_sound_frame[nr] = FrameCounter;
12997 PlaySoundExt(nr, volume, stereo_position, type);
13000 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13002 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13003 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13004 y < LEVELY(BY1) ? LEVELY(BY1) :
13005 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13009 static void PlayLevelSoundAction(int x, int y, int action)
13011 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13014 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13016 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13018 if (sound_effect != SND_UNDEFINED)
13019 PlayLevelSound(x, y, sound_effect);
13022 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13025 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13027 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13028 PlayLevelSound(x, y, sound_effect);
13031 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13033 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13035 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13036 PlayLevelSound(x, y, sound_effect);
13039 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13041 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13043 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13044 StopSound(sound_effect);
13047 static void PlayLevelMusic()
13049 if (levelset.music[level_nr] != MUS_UNDEFINED)
13050 PlayMusic(levelset.music[level_nr]); /* from config file */
13052 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13055 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
13057 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13060 if (sample == SAMPLE_bug)
13061 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
13067 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13071 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13075 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13079 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13083 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13087 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13091 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13094 case SAMPLE_android_clone:
13095 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13098 case SAMPLE_android_move:
13099 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13102 case SAMPLE_spring:
13103 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13107 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
13111 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13114 case SAMPLE_eater_eat:
13115 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13119 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13122 case SAMPLE_collect:
13123 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13126 case SAMPLE_diamond:
13127 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13130 case SAMPLE_squash:
13131 /* !!! CHECK THIS !!! */
13133 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13135 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13139 case SAMPLE_wonderfall:
13140 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13144 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13148 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13152 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13156 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13160 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13164 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13167 case SAMPLE_wonder:
13168 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13172 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13175 case SAMPLE_exit_open:
13176 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13179 case SAMPLE_exit_leave:
13180 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13183 case SAMPLE_dynamite:
13184 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13188 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13192 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13196 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13200 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13204 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13208 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13212 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13217 void RaiseScore(int value)
13219 local_player->score += value;
13221 DrawGameValue_Score(local_player->score);
13224 void RaiseScoreElement(int element)
13229 case EL_BD_DIAMOND:
13230 case EL_EMERALD_YELLOW:
13231 case EL_EMERALD_RED:
13232 case EL_EMERALD_PURPLE:
13233 case EL_SP_INFOTRON:
13234 RaiseScore(level.score[SC_EMERALD]);
13237 RaiseScore(level.score[SC_DIAMOND]);
13240 RaiseScore(level.score[SC_CRYSTAL]);
13243 RaiseScore(level.score[SC_PEARL]);
13246 case EL_BD_BUTTERFLY:
13247 case EL_SP_ELECTRON:
13248 RaiseScore(level.score[SC_BUG]);
13251 case EL_BD_FIREFLY:
13252 case EL_SP_SNIKSNAK:
13253 RaiseScore(level.score[SC_SPACESHIP]);
13256 case EL_DARK_YAMYAM:
13257 RaiseScore(level.score[SC_YAMYAM]);
13260 RaiseScore(level.score[SC_ROBOT]);
13263 RaiseScore(level.score[SC_PACMAN]);
13266 RaiseScore(level.score[SC_NUT]);
13269 case EL_SP_DISK_RED:
13270 case EL_DYNABOMB_INCREASE_NUMBER:
13271 case EL_DYNABOMB_INCREASE_SIZE:
13272 case EL_DYNABOMB_INCREASE_POWER:
13273 RaiseScore(level.score[SC_DYNAMITE]);
13275 case EL_SHIELD_NORMAL:
13276 case EL_SHIELD_DEADLY:
13277 RaiseScore(level.score[SC_SHIELD]);
13279 case EL_EXTRA_TIME:
13280 RaiseScore(level.score[SC_TIME_BONUS]);
13294 RaiseScore(level.score[SC_KEY]);
13297 RaiseScore(element_info[element].collect_score);
13302 void RequestQuitGame(boolean ask_if_really_quit)
13304 if (AllPlayersGone ||
13305 !ask_if_really_quit ||
13306 level_editor_test_game ||
13307 Request("Do you really want to quit the game ?",
13308 REQ_ASK | REQ_STAY_CLOSED))
13310 #if defined(NETWORK_AVALIABLE)
13311 if (options.network)
13312 SendToServer_StopPlaying();
13316 game_status = GAME_MODE_MAIN;
13324 if (tape.playing && tape.deactivate_display)
13325 TapeDeactivateDisplayOff(TRUE);
13328 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13331 if (tape.playing && tape.deactivate_display)
13332 TapeDeactivateDisplayOn();
13339 /* ---------- new game button stuff ---------------------------------------- */
13341 /* graphic position values for game buttons */
13342 #define GAME_BUTTON_XSIZE 30
13343 #define GAME_BUTTON_YSIZE 30
13344 #define GAME_BUTTON_XPOS 5
13345 #define GAME_BUTTON_YPOS 215
13346 #define SOUND_BUTTON_XPOS 5
13347 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13349 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13350 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13351 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13352 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13353 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13354 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13361 } gamebutton_info[NUM_GAME_BUTTONS] =
13364 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13369 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13370 GAME_CTRL_ID_PAUSE,
13374 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13379 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13380 SOUND_CTRL_ID_MUSIC,
13381 "background music on/off"
13384 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13385 SOUND_CTRL_ID_LOOPS,
13386 "sound loops on/off"
13389 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13390 SOUND_CTRL_ID_SIMPLE,
13391 "normal sounds on/off"
13395 void CreateGameButtons()
13399 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13401 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13402 struct GadgetInfo *gi;
13405 unsigned long event_mask;
13406 int gd_xoffset, gd_yoffset;
13407 int gd_x1, gd_x2, gd_y1, gd_y2;
13410 gd_xoffset = gamebutton_info[i].x;
13411 gd_yoffset = gamebutton_info[i].y;
13412 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13413 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13415 if (id == GAME_CTRL_ID_STOP ||
13416 id == GAME_CTRL_ID_PAUSE ||
13417 id == GAME_CTRL_ID_PLAY)
13419 button_type = GD_TYPE_NORMAL_BUTTON;
13421 event_mask = GD_EVENT_RELEASED;
13422 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13423 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13427 button_type = GD_TYPE_CHECK_BUTTON;
13429 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13430 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13431 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13432 event_mask = GD_EVENT_PRESSED;
13433 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13434 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13437 gi = CreateGadget(GDI_CUSTOM_ID, id,
13438 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13439 GDI_X, DX + gd_xoffset,
13440 GDI_Y, DY + gd_yoffset,
13441 GDI_WIDTH, GAME_BUTTON_XSIZE,
13442 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13443 GDI_TYPE, button_type,
13444 GDI_STATE, GD_BUTTON_UNPRESSED,
13445 GDI_CHECKED, checked,
13446 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13447 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13448 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13449 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13450 GDI_EVENT_MASK, event_mask,
13451 GDI_CALLBACK_ACTION, HandleGameButtons,
13455 Error(ERR_EXIT, "cannot create gadget");
13457 game_gadget[id] = gi;
13461 void FreeGameButtons()
13465 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13466 FreeGadget(game_gadget[i]);
13469 static void MapGameButtons()
13473 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13474 MapGadget(game_gadget[i]);
13477 void UnmapGameButtons()
13481 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13482 UnmapGadget(game_gadget[i]);
13485 static void HandleGameButtons(struct GadgetInfo *gi)
13487 int id = gi->custom_id;
13489 if (game_status != GAME_MODE_PLAYING)
13494 case GAME_CTRL_ID_STOP:
13495 RequestQuitGame(TRUE);
13498 case GAME_CTRL_ID_PAUSE:
13499 if (options.network)
13501 #if defined(NETWORK_AVALIABLE)
13503 SendToServer_ContinuePlaying();
13505 SendToServer_PausePlaying();
13509 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13512 case GAME_CTRL_ID_PLAY:
13515 #if defined(NETWORK_AVALIABLE)
13516 if (options.network)
13517 SendToServer_ContinuePlaying();
13521 tape.pausing = FALSE;
13522 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13527 case SOUND_CTRL_ID_MUSIC:
13528 if (setup.sound_music)
13530 setup.sound_music = FALSE;
13533 else if (audio.music_available)
13535 setup.sound = setup.sound_music = TRUE;
13537 SetAudioMode(setup.sound);
13543 case SOUND_CTRL_ID_LOOPS:
13544 if (setup.sound_loops)
13545 setup.sound_loops = FALSE;
13546 else if (audio.loops_available)
13548 setup.sound = setup.sound_loops = TRUE;
13549 SetAudioMode(setup.sound);
13553 case SOUND_CTRL_ID_SIMPLE:
13554 if (setup.sound_simple)
13555 setup.sound_simple = FALSE;
13556 else if (audio.sound_available)
13558 setup.sound = setup.sound_simple = TRUE;
13559 SetAudioMode(setup.sound);