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 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
42 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
49 /* for MovePlayer() */
50 #define MP_NO_ACTION 0
53 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
55 /* for ScrollPlayer() */
57 #define SCROLL_GO_ON 1
59 /* for Bang()/Explode() */
60 #define EX_PHASE_START 0
61 #define EX_TYPE_NONE 0
62 #define EX_TYPE_NORMAL (1 << 0)
63 #define EX_TYPE_CENTER (1 << 1)
64 #define EX_TYPE_BORDER (1 << 2)
65 #define EX_TYPE_CROSS (1 << 3)
66 #define EX_TYPE_DYNA (1 << 4)
67 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
69 /* special positions in the game control window (relative to control window) */
72 #define XX_EMERALDS 29
73 #define YY_EMERALDS 54
74 #define XX_DYNAMITE 29
75 #define YY_DYNAMITE 89
84 /* special positions in the game control window (relative to main window) */
85 #define DX_LEVEL (DX + XX_LEVEL)
86 #define DY_LEVEL (DY + YY_LEVEL)
87 #define DX_EMERALDS (DX + XX_EMERALDS)
88 #define DY_EMERALDS (DY + YY_EMERALDS)
89 #define DX_DYNAMITE (DX + XX_DYNAMITE)
90 #define DY_DYNAMITE (DY + YY_DYNAMITE)
91 #define DX_KEYS (DX + XX_KEYS)
92 #define DY_KEYS (DY + YY_KEYS)
93 #define DX_SCORE (DX + XX_SCORE)
94 #define DY_SCORE (DY + YY_SCORE)
95 #define DX_TIME1 (DX + XX_TIME1)
96 #define DX_TIME2 (DX + XX_TIME2)
97 #define DY_TIME (DY + YY_TIME)
99 /* values for initial player move delay (initial delay counter value) */
100 #define INITIAL_MOVE_DELAY_OFF -1
101 #define INITIAL_MOVE_DELAY_ON 0
103 /* values for player movement speed (which is in fact a delay value) */
104 #define MOVE_DELAY_MIN_SPEED 32
105 #define MOVE_DELAY_NORMAL_SPEED 8
106 #define MOVE_DELAY_HIGH_SPEED 4
107 #define MOVE_DELAY_MAX_SPEED 1
110 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
111 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
113 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
114 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
116 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
117 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
119 /* values for other actions */
120 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
121 #define MOVE_STEPSIZE_MIN (1)
122 #define MOVE_STEPSIZE_MAX (TILEX)
124 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
125 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
127 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
129 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
130 RND(element_info[e].push_delay_random))
131 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
132 RND(element_info[e].drop_delay_random))
133 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
134 RND(element_info[e].move_delay_random))
135 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
136 (element_info[e].move_delay_random))
137 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
138 RND(element_info[e].ce_value_random_initial))
139 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
140 RND((c)->delay_random * (c)->delay_frames))
141 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
142 RND((c)->delay_random))
144 #define GET_TARGET_ELEMENT(e, ch) \
145 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
146 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
148 #define CAN_GROW_INTO(e) \
149 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
155 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
157 (CAN_MOVE_INTO_ACID(e) && \
158 Feld[x][y] == EL_ACID) || \
161 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
163 (CAN_MOVE_INTO_ACID(e) && \
164 Feld[x][y] == EL_ACID) || \
167 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
168 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
170 (CAN_MOVE_INTO_ACID(e) && \
171 Feld[x][y] == EL_ACID) || \
172 (DONT_COLLIDE_WITH(e) && \
174 !PLAYER_ENEMY_PROTECTED(x, y))))
176 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
179 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
180 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
182 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
183 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
185 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
186 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
188 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
189 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
191 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
192 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
194 #define PIG_CAN_ENTER_FIELD(e, x, y) \
195 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
197 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
198 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
199 IS_FOOD_PENGUIN(Feld[x][y])))
200 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
203 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
204 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
206 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
210 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
211 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
213 #define GROUP_NR(e) ((e) - EL_GROUP_START)
214 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
215 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
216 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
218 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
219 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
221 #define CE_ENTER_FIELD_COND(e, x, y) \
222 (!IS_PLAYER(x, y) && \
223 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
225 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
226 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
228 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
229 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
231 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
232 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
233 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
234 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
236 /* game button identifiers */
237 #define GAME_CTRL_ID_STOP 0
238 #define GAME_CTRL_ID_PAUSE 1
239 #define GAME_CTRL_ID_PLAY 2
240 #define SOUND_CTRL_ID_MUSIC 3
241 #define SOUND_CTRL_ID_LOOPS 4
242 #define SOUND_CTRL_ID_SIMPLE 5
244 #define NUM_GAME_BUTTONS 6
247 /* forward declaration for internal use */
249 static void CreateField(int, int, int);
251 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
252 static void AdvanceFrameAndPlayerCounters(int);
254 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
255 static boolean MovePlayer(struct PlayerInfo *, int, int);
256 static void ScrollPlayer(struct PlayerInfo *, int);
257 static void ScrollScreen(struct PlayerInfo *, int);
259 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
261 static void InitBeltMovement(void);
262 static void CloseAllOpenTimegates(void);
263 static void CheckGravityMovement(struct PlayerInfo *);
264 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
265 static void KillPlayerUnlessEnemyProtected(int, int);
266 static void KillPlayerUnlessExplosionProtected(int, int);
268 static void TestIfPlayerTouchesCustomElement(int, int);
269 static void TestIfElementTouchesCustomElement(int, int);
270 static void TestIfElementHitsCustomElement(int, int, int);
272 static void TestIfElementSmashesCustomElement(int, int, int);
275 static void HandleElementChange(int, int, int);
277 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
278 #define CheckTriggeredElementChange(x, y, e, ev) \
279 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
280 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
281 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
282 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
283 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
284 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
285 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
287 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
288 #define CheckElementChange(x, y, e, te, ev) \
289 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
290 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
291 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
292 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
293 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
295 static void PlayLevelSound(int, int, int);
296 static void PlayLevelSoundNearest(int, int, int);
297 static void PlayLevelSoundAction(int, int, int);
298 static void PlayLevelSoundElementAction(int, int, int, int);
299 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
300 static void PlayLevelSoundActionIfLoop(int, int, int);
301 static void StopLevelSoundActionIfLoop(int, int, int);
302 static void PlayLevelMusic();
304 static void MapGameButtons();
305 static void HandleGameButtons(struct GadgetInfo *);
307 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
310 /* ------------------------------------------------------------------------- */
311 /* definition of elements that automatically change to other elements after */
312 /* a specified time, eventually calling a function when changing */
313 /* ------------------------------------------------------------------------- */
315 /* forward declaration for changer functions */
316 static void InitBuggyBase(int, int);
317 static void WarnBuggyBase(int, int);
319 static void InitTrap(int, int);
320 static void ActivateTrap(int, int);
321 static void ChangeActiveTrap(int, int);
323 static void InitRobotWheel(int, int);
324 static void RunRobotWheel(int, int);
325 static void StopRobotWheel(int, int);
327 static void InitTimegateWheel(int, int);
328 static void RunTimegateWheel(int, int);
330 static void InitMagicBallDelay(int, int);
331 static void ActivateMagicBall(int, int);
333 struct ChangingElementInfo
338 void (*pre_change_function)(int x, int y);
339 void (*change_function)(int x, int y);
340 void (*post_change_function)(int x, int y);
343 static struct ChangingElementInfo change_delay_list[] =
394 EL_SWITCHGATE_OPENING,
402 EL_SWITCHGATE_CLOSING,
403 EL_SWITCHGATE_CLOSED,
435 EL_ACID_SPLASH_RIGHT,
444 EL_SP_BUGGY_BASE_ACTIVATING,
451 EL_SP_BUGGY_BASE_ACTIVATING,
452 EL_SP_BUGGY_BASE_ACTIVE,
459 EL_SP_BUGGY_BASE_ACTIVE,
483 EL_ROBOT_WHEEL_ACTIVE,
491 EL_TIMEGATE_SWITCH_ACTIVE,
499 EL_EMC_MAGIC_BALL_ACTIVE,
500 EL_EMC_MAGIC_BALL_ACTIVE,
507 EL_EMC_SPRING_BUMPER_ACTIVE,
508 EL_EMC_SPRING_BUMPER,
528 int push_delay_fixed, push_delay_random;
533 { EL_BALLOON, 0, 0 },
535 { EL_SOKOBAN_OBJECT, 2, 0 },
536 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
537 { EL_SATELLITE, 2, 0 },
538 { EL_SP_DISK_YELLOW, 2, 0 },
540 { EL_UNDEFINED, 0, 0 },
548 move_stepsize_list[] =
550 { EL_AMOEBA_DROP, 2 },
551 { EL_AMOEBA_DROPPING, 2 },
552 { EL_QUICKSAND_FILLING, 1 },
553 { EL_QUICKSAND_EMPTYING, 1 },
554 { EL_MAGIC_WALL_FILLING, 2 },
555 { EL_BD_MAGIC_WALL_FILLING, 2 },
556 { EL_MAGIC_WALL_EMPTYING, 2 },
557 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
567 collect_count_list[] =
570 { EL_BD_DIAMOND, 1 },
571 { EL_EMERALD_YELLOW, 1 },
572 { EL_EMERALD_RED, 1 },
573 { EL_EMERALD_PURPLE, 1 },
575 { EL_SP_INFOTRON, 1 },
587 access_direction_list[] =
589 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
590 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
591 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
592 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
593 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
594 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
595 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
596 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
597 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
598 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
599 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
601 { EL_SP_PORT_LEFT, MV_RIGHT },
602 { EL_SP_PORT_RIGHT, MV_LEFT },
603 { EL_SP_PORT_UP, MV_DOWN },
604 { EL_SP_PORT_DOWN, MV_UP },
605 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
606 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
607 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
608 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
609 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
610 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
611 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
612 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
613 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
614 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
615 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
616 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
617 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
618 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
619 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
621 { EL_UNDEFINED, MV_NONE }
624 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
626 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
627 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
628 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
629 IS_JUST_CHANGING(x, y))
631 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
633 /* static variables for playfield scan mode (scanning forward or backward) */
634 static int playfield_scan_start_x = 0;
635 static int playfield_scan_start_y = 0;
636 static int playfield_scan_delta_x = 1;
637 static int playfield_scan_delta_y = 1;
639 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
640 (y) >= 0 && (y) <= lev_fieldy - 1; \
641 (y) += playfield_scan_delta_y) \
642 for ((x) = playfield_scan_start_x; \
643 (x) >= 0 && (x) <= lev_fieldx - 1; \
644 (x) += playfield_scan_delta_x) \
646 static void InitPlayfieldScanModeVars()
648 if (game.use_reverse_scan_direction)
650 playfield_scan_start_x = lev_fieldx - 1;
651 playfield_scan_start_y = lev_fieldy - 1;
653 playfield_scan_delta_x = -1;
654 playfield_scan_delta_y = -1;
658 playfield_scan_start_x = 0;
659 playfield_scan_start_y = 0;
661 playfield_scan_delta_x = 1;
662 playfield_scan_delta_y = 1;
666 static void InitPlayfieldScanMode(int mode)
668 game.use_reverse_scan_direction =
669 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
671 InitPlayfieldScanModeVars();
674 static int get_move_delay_from_stepsize(int move_stepsize)
677 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
679 /* make sure that stepsize value is always a power of 2 */
680 move_stepsize = (1 << log_2(move_stepsize));
682 return TILEX / move_stepsize;
685 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
688 int move_delay = get_move_delay_from_stepsize(move_stepsize);
689 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
691 /* do no immediately change move delay -- the player might just be moving */
692 player->move_delay_value_next = move_delay;
694 /* information if player can move must be set separately */
695 player->cannot_move = cannot_move;
699 player->move_delay = game.initial_move_delay;
700 player->move_delay_value = game.initial_move_delay_value;
702 player->move_delay_value_next = -1;
704 player->move_delay_reset_counter = 0;
708 void GetPlayerConfig()
710 if (!audio.sound_available)
711 setup.sound_simple = FALSE;
713 if (!audio.loops_available)
714 setup.sound_loops = FALSE;
716 if (!audio.music_available)
717 setup.sound_music = FALSE;
719 if (!video.fullscreen_available)
720 setup.fullscreen = FALSE;
722 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
724 SetAudioMode(setup.sound);
728 static int getBeltNrFromBeltElement(int element)
730 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
731 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
732 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
735 static int getBeltNrFromBeltActiveElement(int element)
737 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
738 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
739 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
742 static int getBeltNrFromBeltSwitchElement(int element)
744 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
745 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
746 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
749 static int getBeltDirNrFromBeltSwitchElement(int element)
751 static int belt_base_element[4] =
753 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
754 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
755 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
756 EL_CONVEYOR_BELT_4_SWITCH_LEFT
759 int belt_nr = getBeltNrFromBeltSwitchElement(element);
760 int belt_dir_nr = element - belt_base_element[belt_nr];
762 return (belt_dir_nr % 3);
765 static int getBeltDirFromBeltSwitchElement(int element)
767 static int belt_move_dir[3] =
774 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
776 return belt_move_dir[belt_dir_nr];
779 static void InitPlayerField(int x, int y, int element, boolean init_game)
781 if (element == EL_SP_MURPHY)
785 if (stored_player[0].present)
787 Feld[x][y] = EL_SP_MURPHY_CLONE;
793 stored_player[0].use_murphy = TRUE;
796 Feld[x][y] = EL_PLAYER_1;
802 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
803 int jx = player->jx, jy = player->jy;
805 player->present = TRUE;
807 player->block_last_field = (element == EL_SP_MURPHY ?
808 level.sp_block_last_field :
809 level.block_last_field);
811 /* ---------- initialize player's last field block delay --------------- */
813 /* always start with reliable default value (no adjustment needed) */
814 player->block_delay_adjustment = 0;
816 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
817 if (player->block_last_field && element == EL_SP_MURPHY)
818 player->block_delay_adjustment = 1;
820 /* special case 2: in game engines before 3.1.1, blocking was different */
821 if (game.use_block_last_field_bug)
822 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
824 if (!options.network || player->connected)
826 player->active = TRUE;
828 /* remove potentially duplicate players */
829 if (StorePlayer[jx][jy] == Feld[x][y])
830 StorePlayer[jx][jy] = 0;
832 StorePlayer[x][y] = Feld[x][y];
836 printf("Player %d activated.\n", player->element_nr);
837 printf("[Local player is %d and currently %s.]\n",
838 local_player->element_nr,
839 local_player->active ? "active" : "not active");
843 Feld[x][y] = EL_EMPTY;
845 player->jx = player->last_jx = x;
846 player->jy = player->last_jy = y;
850 static void InitField(int x, int y, boolean init_game)
852 int element = Feld[x][y];
861 InitPlayerField(x, y, element, init_game);
864 case EL_SOKOBAN_FIELD_PLAYER:
865 element = Feld[x][y] = EL_PLAYER_1;
866 InitField(x, y, init_game);
868 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
869 InitField(x, y, init_game);
872 case EL_SOKOBAN_FIELD_EMPTY:
873 local_player->sokobanfields_still_needed++;
877 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
878 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
879 else if (x > 0 && Feld[x-1][y] == EL_ACID)
880 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
881 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
882 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
883 else if (y > 0 && Feld[x][y-1] == EL_ACID)
884 Feld[x][y] = EL_ACID_POOL_BOTTOM;
885 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
886 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
894 case EL_SPACESHIP_RIGHT:
895 case EL_SPACESHIP_UP:
896 case EL_SPACESHIP_LEFT:
897 case EL_SPACESHIP_DOWN:
899 case EL_BD_BUTTERFLY_RIGHT:
900 case EL_BD_BUTTERFLY_UP:
901 case EL_BD_BUTTERFLY_LEFT:
902 case EL_BD_BUTTERFLY_DOWN:
903 case EL_BD_BUTTERFLY:
904 case EL_BD_FIREFLY_RIGHT:
905 case EL_BD_FIREFLY_UP:
906 case EL_BD_FIREFLY_LEFT:
907 case EL_BD_FIREFLY_DOWN:
909 case EL_PACMAN_RIGHT:
933 if (y == lev_fieldy - 1)
935 Feld[x][y] = EL_AMOEBA_GROWING;
936 Store[x][y] = EL_AMOEBA_WET;
940 case EL_DYNAMITE_ACTIVE:
941 case EL_SP_DISK_RED_ACTIVE:
942 case EL_DYNABOMB_PLAYER_1_ACTIVE:
943 case EL_DYNABOMB_PLAYER_2_ACTIVE:
944 case EL_DYNABOMB_PLAYER_3_ACTIVE:
945 case EL_DYNABOMB_PLAYER_4_ACTIVE:
950 local_player->lights_still_needed++;
954 local_player->friends_still_needed++;
959 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
962 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
963 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
964 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
965 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
966 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
967 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
968 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
969 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
970 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
971 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
972 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
973 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
976 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
977 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
978 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
980 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
982 game.belt_dir[belt_nr] = belt_dir;
983 game.belt_dir_nr[belt_nr] = belt_dir_nr;
985 else /* more than one switch -- set it like the first switch */
987 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
992 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
994 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
997 case EL_LIGHT_SWITCH_ACTIVE:
999 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1002 case EL_EMC_MAGIC_BALL:
1003 if (game.ball_state)
1004 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1007 case EL_EMC_MAGIC_BALL_SWITCH:
1008 if (game.ball_state)
1009 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1014 if (IS_CUSTOM_ELEMENT(element))
1016 if (CAN_MOVE(element))
1019 #if USE_NEW_CUSTOM_VALUE
1020 if (!element_info[element].use_last_ce_value || init_game)
1021 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1025 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1028 else if (IS_GROUP_ELEMENT(element))
1030 struct ElementGroupInfo *group = element_info[element].group;
1031 int last_anim_random_frame = gfx.anim_random_frame;
1034 if (group->choice_mode == ANIM_RANDOM)
1035 gfx.anim_random_frame = RND(group->num_elements_resolved);
1037 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1038 group->choice_mode, 0,
1041 if (group->choice_mode == ANIM_RANDOM)
1042 gfx.anim_random_frame = last_anim_random_frame;
1044 group->choice_pos++;
1046 Feld[x][y] = group->element_resolved[element_pos];
1048 InitField(x, y, init_game);
1055 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1060 #if USE_NEW_CUSTOM_VALUE
1063 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1065 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1073 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1075 InitField(x, y, init_game);
1077 /* not needed to call InitMovDir() -- already done by InitField()! */
1078 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1079 CAN_MOVE(Feld[x][y]))
1083 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1085 int old_element = Feld[x][y];
1087 InitField(x, y, init_game);
1089 /* not needed to call InitMovDir() -- already done by InitField()! */
1090 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1091 CAN_MOVE(old_element) &&
1092 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1095 /* this case is in fact a combination of not less than three bugs:
1096 first, it calls InitMovDir() for elements that can move, although this is
1097 already done by InitField(); then, it checks the element that was at this
1098 field _before_ the call to InitField() (which can change it); lastly, it
1099 was not called for "mole with direction" elements, which were treated as
1100 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1104 inline void DrawGameValue_Emeralds(int value)
1106 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1109 inline void DrawGameValue_Dynamite(int value)
1111 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1114 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1118 /* currently only 4 of 8 possible keys are displayed */
1119 for (i = 0; i < STD_NUM_KEYS; i++)
1122 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1123 el2edimg(EL_KEY_1 + i));
1125 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1126 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1127 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1131 inline void DrawGameValue_Score(int value)
1133 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1136 inline void DrawGameValue_Time(int value)
1139 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1141 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1144 inline void DrawGameValue_Level(int value)
1147 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1150 /* misuse area for displaying emeralds to draw bigger level number */
1151 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1152 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1154 /* now copy it to the area for displaying level number */
1155 BlitBitmap(drawto, drawto,
1156 DX_EMERALDS, DY_EMERALDS + 1,
1157 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1158 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1159 DX_LEVEL - 1, DY_LEVEL + 1);
1161 /* restore the area for displaying emeralds */
1162 DrawGameValue_Emeralds(local_player->gems_still_needed);
1164 /* yes, this is all really ugly :-) */
1168 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1171 int key[MAX_NUM_KEYS];
1174 for (i = 0; i < MAX_NUM_KEYS; i++)
1175 key[i] = key_bits & (1 << i);
1177 DrawGameValue_Level(level_nr);
1179 DrawGameValue_Emeralds(emeralds);
1180 DrawGameValue_Dynamite(dynamite);
1181 DrawGameValue_Score(score);
1182 DrawGameValue_Time(time);
1184 DrawGameValue_Keys(key);
1187 void DrawGameDoorValues()
1191 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1193 DrawGameDoorValues_EM();
1198 DrawGameValue_Level(level_nr);
1200 DrawGameValue_Emeralds(local_player->gems_still_needed);
1201 DrawGameValue_Dynamite(local_player->inventory_size);
1202 DrawGameValue_Score(local_player->score);
1203 DrawGameValue_Time(TimeLeft);
1205 for (i = 0; i < MAX_PLAYERS; i++)
1206 DrawGameValue_Keys(stored_player[i].key);
1209 static void resolve_group_element(int group_element, int recursion_depth)
1211 static int group_nr;
1212 static struct ElementGroupInfo *group;
1213 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1216 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1218 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1219 group_element - EL_GROUP_START + 1);
1221 /* replace element which caused too deep recursion by question mark */
1222 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1227 if (recursion_depth == 0) /* initialization */
1229 group = element_info[group_element].group;
1230 group_nr = group_element - EL_GROUP_START;
1232 group->num_elements_resolved = 0;
1233 group->choice_pos = 0;
1236 for (i = 0; i < actual_group->num_elements; i++)
1238 int element = actual_group->element[i];
1240 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1243 if (IS_GROUP_ELEMENT(element))
1244 resolve_group_element(element, recursion_depth + 1);
1247 group->element_resolved[group->num_elements_resolved++] = element;
1248 element_info[element].in_group[group_nr] = TRUE;
1255 =============================================================================
1257 -----------------------------------------------------------------------------
1258 initialize game engine due to level / tape version number
1259 =============================================================================
1262 static void InitGameEngine()
1264 int i, j, k, l, x, y;
1266 /* set game engine from tape file when re-playing, else from level file */
1267 game.engine_version = (tape.playing ? tape.engine_version :
1268 level.game_version);
1270 /* ---------------------------------------------------------------------- */
1271 /* set flags for bugs and changes according to active game engine version */
1272 /* ---------------------------------------------------------------------- */
1275 Summary of bugfix/change:
1276 Fixed handling for custom elements that change when pushed by the player.
1278 Fixed/changed in version:
1282 Before 3.1.0, custom elements that "change when pushing" changed directly
1283 after the player started pushing them (until then handled in "DigField()").
1284 Since 3.1.0, these custom elements are not changed until the "pushing"
1285 move of the element is finished (now handled in "ContinueMoving()").
1287 Affected levels/tapes:
1288 The first condition is generally needed for all levels/tapes before version
1289 3.1.0, which might use the old behaviour before it was changed; known tapes
1290 that are affected are some tapes from the level set "Walpurgis Gardens" by
1292 The second condition is an exception from the above case and is needed for
1293 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1294 above (including some development versions of 3.1.0), but before it was
1295 known that this change would break tapes like the above and was fixed in
1296 3.1.1, so that the changed behaviour was active although the engine version
1297 while recording maybe was before 3.1.0. There is at least one tape that is
1298 affected by this exception, which is the tape for the one-level set "Bug
1299 Machine" by Juergen Bonhagen.
1302 game.use_change_when_pushing_bug =
1303 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1305 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1306 tape.game_version < VERSION_IDENT(3,1,1,0)));
1309 Summary of bugfix/change:
1310 Fixed handling for blocking the field the player leaves when moving.
1312 Fixed/changed in version:
1316 Before 3.1.1, when "block last field when moving" was enabled, the field
1317 the player is leaving when moving was blocked for the time of the move,
1318 and was directly unblocked afterwards. This resulted in the last field
1319 being blocked for exactly one less than the number of frames of one player
1320 move. Additionally, even when blocking was disabled, the last field was
1321 blocked for exactly one frame.
1322 Since 3.1.1, due to changes in player movement handling, the last field
1323 is not blocked at all when blocking is disabled. When blocking is enabled,
1324 the last field is blocked for exactly the number of frames of one player
1325 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1326 last field is blocked for exactly one more than the number of frames of
1329 Affected levels/tapes:
1330 (!!! yet to be determined -- probably many !!!)
1333 game.use_block_last_field_bug =
1334 (game.engine_version < VERSION_IDENT(3,1,1,0));
1337 Summary of bugfix/change:
1338 Changed behaviour of CE changes with multiple changes per single frame.
1340 Fixed/changed in version:
1344 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1345 This resulted in race conditions where CEs seem to behave strange in some
1346 situations (where triggered CE changes were just skipped because there was
1347 already a CE change on that tile in the playfield in that engine frame).
1348 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1349 (The number of changes per frame must be limited in any case, because else
1350 it is easily possible to define CE changes that would result in an infinite
1351 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1352 should be set large enough so that it would only be reached in cases where
1353 the corresponding CE change conditions run into a loop. Therefore, it seems
1354 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1355 maximal number of change pages for custom elements.)
1357 Affected levels/tapes:
1361 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1362 game.max_num_changes_per_frame = 1;
1364 game.max_num_changes_per_frame =
1365 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1368 /* ---------------------------------------------------------------------- */
1370 /* default scan direction: scan playfield from top/left to bottom/right */
1371 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1373 /* dynamically adjust element properties according to game engine version */
1374 InitElementPropertiesEngine(game.engine_version);
1377 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1378 printf(" tape version == %06d [%s] [file: %06d]\n",
1379 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1381 printf(" => game.engine_version == %06d\n", game.engine_version);
1384 /* ---------- recursively resolve group elements ------------------------- */
1386 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1387 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1388 element_info[i].in_group[j] = FALSE;
1390 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1391 resolve_group_element(EL_GROUP_START + i, 0);
1393 /* ---------- initialize player's initial move delay --------------------- */
1396 /* dynamically adjust player properties according to level information */
1397 game.initial_move_delay_value =
1398 get_move_delay_from_stepsize(level.initial_player_stepsize);
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 /* dynamically adjust player properties according to game engine version */
1406 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1407 game.initial_move_delay_value : 0);
1409 /* ---------- initialize player's initial push delay --------------------- */
1411 /* dynamically adjust player properties according to game engine version */
1412 game.initial_push_delay_value =
1413 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1415 /* ---------- initialize changing elements ------------------------------- */
1417 /* initialize changing elements information */
1418 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1420 struct ElementInfo *ei = &element_info[i];
1422 /* this pointer might have been changed in the level editor */
1423 ei->change = &ei->change_page[0];
1425 if (!IS_CUSTOM_ELEMENT(i))
1427 ei->change->target_element = EL_EMPTY_SPACE;
1428 ei->change->delay_fixed = 0;
1429 ei->change->delay_random = 0;
1430 ei->change->delay_frames = 1;
1433 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1435 ei->has_change_event[j] = FALSE;
1437 ei->event_page_nr[j] = 0;
1438 ei->event_page[j] = &ei->change_page[0];
1442 /* add changing elements from pre-defined list */
1443 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1445 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1446 struct ElementInfo *ei = &element_info[ch_delay->element];
1448 ei->change->target_element = ch_delay->target_element;
1449 ei->change->delay_fixed = ch_delay->change_delay;
1451 ei->change->pre_change_function = ch_delay->pre_change_function;
1452 ei->change->change_function = ch_delay->change_function;
1453 ei->change->post_change_function = ch_delay->post_change_function;
1455 ei->change->can_change = TRUE;
1456 ei->change->can_change_or_has_action = TRUE;
1458 ei->has_change_event[CE_DELAY] = TRUE;
1460 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1461 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1464 /* ---------- initialize internal run-time variables ------------- */
1466 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1468 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1470 for (j = 0; j < ei->num_change_pages; j++)
1472 ei->change_page[j].can_change_or_has_action =
1473 (ei->change_page[j].can_change |
1474 ei->change_page[j].has_action);
1478 /* add change events from custom element configuration */
1479 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1481 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1483 for (j = 0; j < ei->num_change_pages; j++)
1485 if (!ei->change_page[j].can_change_or_has_action)
1488 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1490 /* only add event page for the first page found with this event */
1491 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1493 ei->has_change_event[k] = TRUE;
1495 ei->event_page_nr[k] = j;
1496 ei->event_page[k] = &ei->change_page[j];
1502 /* ---------- initialize run-time trigger player and element ------------- */
1504 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1506 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1508 for (j = 0; j < ei->num_change_pages; j++)
1510 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1511 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1512 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1513 ei->change_page[j].actual_trigger_ce_value = 0;
1517 /* ---------- initialize trigger events ---------------------------------- */
1519 /* initialize trigger events information */
1520 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1521 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1522 trigger_events[i][j] = FALSE;
1524 /* add trigger events from element change event properties */
1525 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1527 struct ElementInfo *ei = &element_info[i];
1529 for (j = 0; j < ei->num_change_pages; j++)
1531 if (!ei->change_page[j].can_change_or_has_action)
1534 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1536 int trigger_element = ei->change_page[j].trigger_element;
1538 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1540 if (ei->change_page[j].has_event[k])
1542 if (IS_GROUP_ELEMENT(trigger_element))
1544 struct ElementGroupInfo *group =
1545 element_info[trigger_element].group;
1547 for (l = 0; l < group->num_elements_resolved; l++)
1548 trigger_events[group->element_resolved[l]][k] = TRUE;
1551 trigger_events[trigger_element][k] = TRUE;
1558 /* ---------- initialize push delay -------------------------------------- */
1560 /* initialize push delay values to default */
1561 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1563 if (!IS_CUSTOM_ELEMENT(i))
1565 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1566 element_info[i].push_delay_random = game.default_push_delay_random;
1570 /* set push delay value for certain elements from pre-defined list */
1571 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1573 int e = push_delay_list[i].element;
1575 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1576 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1579 /* set push delay value for Supaplex elements for newer engine versions */
1580 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1582 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1584 if (IS_SP_ELEMENT(i))
1586 /* set SP push delay to just enough to push under a falling zonk */
1587 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1589 element_info[i].push_delay_fixed = delay;
1590 element_info[i].push_delay_random = 0;
1595 /* ---------- initialize move stepsize ----------------------------------- */
1597 /* initialize move stepsize values to default */
1598 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1599 if (!IS_CUSTOM_ELEMENT(i))
1600 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1602 /* set move stepsize value for certain elements from pre-defined list */
1603 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1605 int e = move_stepsize_list[i].element;
1607 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1610 /* ---------- initialize collect score ----------------------------------- */
1612 /* initialize collect score values for custom elements from initial value */
1613 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1614 if (IS_CUSTOM_ELEMENT(i))
1615 element_info[i].collect_score = element_info[i].collect_score_initial;
1617 /* ---------- initialize collect count ----------------------------------- */
1619 /* initialize collect count values for non-custom elements */
1620 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1621 if (!IS_CUSTOM_ELEMENT(i))
1622 element_info[i].collect_count_initial = 0;
1624 /* add collect count values for all elements from pre-defined list */
1625 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1626 element_info[collect_count_list[i].element].collect_count_initial =
1627 collect_count_list[i].count;
1629 /* ---------- initialize access direction -------------------------------- */
1631 /* initialize access direction values to default (access from every side) */
1632 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1633 if (!IS_CUSTOM_ELEMENT(i))
1634 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1636 /* set access direction value for certain elements from pre-defined list */
1637 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1638 element_info[access_direction_list[i].element].access_direction =
1639 access_direction_list[i].direction;
1641 /* ---------- initialize explosion content ------------------------------- */
1642 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1644 if (IS_CUSTOM_ELEMENT(i))
1647 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1649 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1651 element_info[i].content.e[x][y] =
1652 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1653 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1654 i == EL_PLAYER_3 ? EL_EMERALD :
1655 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1656 i == EL_MOLE ? EL_EMERALD_RED :
1657 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1658 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1659 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1660 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1661 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1662 i == EL_WALL_EMERALD ? EL_EMERALD :
1663 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1664 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1665 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1666 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1667 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1668 i == EL_WALL_PEARL ? EL_PEARL :
1669 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1675 int get_num_special_action(int element, int action_first, int action_last)
1677 int num_special_action = 0;
1680 for (i = action_first; i <= action_last; i++)
1682 boolean found = FALSE;
1684 for (j = 0; j < NUM_DIRECTIONS; j++)
1685 if (el_act_dir2img(element, i, j) !=
1686 el_act_dir2img(element, ACTION_DEFAULT, j))
1690 num_special_action++;
1695 return num_special_action;
1699 =============================================================================
1701 -----------------------------------------------------------------------------
1702 initialize and start new game
1703 =============================================================================
1708 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1709 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1710 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1715 /* don't play tapes over network */
1716 network_playing = (options.network && !tape.playing);
1718 for (i = 0; i < MAX_PLAYERS; i++)
1720 struct PlayerInfo *player = &stored_player[i];
1722 player->index_nr = i;
1723 player->index_bit = (1 << i);
1724 player->element_nr = EL_PLAYER_1 + i;
1726 player->present = FALSE;
1727 player->active = FALSE;
1730 player->effective_action = 0;
1731 player->programmed_action = 0;
1734 player->gems_still_needed = level.gems_needed;
1735 player->sokobanfields_still_needed = 0;
1736 player->lights_still_needed = 0;
1737 player->friends_still_needed = 0;
1739 for (j = 0; j < MAX_NUM_KEYS; j++)
1740 player->key[j] = FALSE;
1742 player->dynabomb_count = 0;
1743 player->dynabomb_size = 1;
1744 player->dynabombs_left = 0;
1745 player->dynabomb_xl = FALSE;
1747 player->MovDir = MV_NONE;
1750 player->GfxDir = MV_NONE;
1751 player->GfxAction = ACTION_DEFAULT;
1753 player->StepFrame = 0;
1755 player->use_murphy = FALSE;
1756 player->artwork_element =
1757 (level.use_artwork_element[i] ? level.artwork_element[i] :
1758 player->element_nr);
1760 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1761 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1763 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1765 player->actual_frame_counter = 0;
1767 player->step_counter = 0;
1769 player->last_move_dir = MV_NONE;
1771 player->is_waiting = FALSE;
1772 player->is_moving = FALSE;
1773 player->is_auto_moving = FALSE;
1774 player->is_digging = FALSE;
1775 player->is_snapping = FALSE;
1776 player->is_collecting = FALSE;
1777 player->is_pushing = FALSE;
1778 player->is_switching = FALSE;
1779 player->is_dropping = FALSE;
1781 player->is_bored = FALSE;
1782 player->is_sleeping = FALSE;
1784 player->frame_counter_bored = -1;
1785 player->frame_counter_sleeping = -1;
1787 player->anim_delay_counter = 0;
1788 player->post_delay_counter = 0;
1790 player->action_waiting = ACTION_DEFAULT;
1791 player->last_action_waiting = ACTION_DEFAULT;
1792 player->special_action_bored = ACTION_DEFAULT;
1793 player->special_action_sleeping = ACTION_DEFAULT;
1795 /* set number of special actions for bored and sleeping animation */
1796 player->num_special_action_bored =
1797 get_num_special_action(player->artwork_element,
1798 ACTION_BORING_1, ACTION_BORING_LAST);
1799 player->num_special_action_sleeping =
1800 get_num_special_action(player->artwork_element,
1801 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1803 player->switch_x = -1;
1804 player->switch_y = -1;
1806 player->drop_x = -1;
1807 player->drop_y = -1;
1809 player->show_envelope = 0;
1812 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1814 player->move_delay = game.initial_move_delay;
1815 player->move_delay_value = game.initial_move_delay_value;
1817 player->move_delay_value_next = -1;
1819 player->move_delay_reset_counter = 0;
1821 player->cannot_move = FALSE;
1824 player->push_delay = -1; /* initialized when pushing starts */
1825 player->push_delay_value = game.initial_push_delay_value;
1827 player->drop_delay = 0;
1829 player->last_jx = player->last_jy = 0;
1830 player->jx = player->jy = 0;
1832 player->shield_normal_time_left = 0;
1833 player->shield_deadly_time_left = 0;
1835 player->inventory_infinite_element = EL_UNDEFINED;
1836 player->inventory_size = 0;
1838 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1839 SnapField(player, 0, 0);
1841 player->LevelSolved = FALSE;
1842 player->GameOver = FALSE;
1845 network_player_action_received = FALSE;
1847 #if defined(NETWORK_AVALIABLE)
1848 /* initial null action */
1849 if (network_playing)
1850 SendToServer_MovePlayer(MV_NONE);
1859 TimeLeft = level.time;
1862 ScreenMovDir = MV_NONE;
1866 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1868 AllPlayersGone = FALSE;
1870 game.yamyam_content_nr = 0;
1871 game.magic_wall_active = FALSE;
1872 game.magic_wall_time_left = 0;
1873 game.light_time_left = 0;
1874 game.timegate_time_left = 0;
1875 game.switchgate_pos = 0;
1876 game.wind_direction = level.wind_direction_initial;
1877 game.gravity = level.initial_gravity;
1878 game.explosions_delayed = TRUE;
1880 game.lenses_time_left = 0;
1881 game.magnify_time_left = 0;
1883 game.ball_state = level.ball_state_initial;
1884 game.ball_content_nr = 0;
1886 game.envelope_active = FALSE;
1888 for (i = 0; i < NUM_BELTS; i++)
1890 game.belt_dir[i] = MV_NONE;
1891 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1894 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1895 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1898 SCAN_PLAYFIELD(x, y)
1900 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1903 Feld[x][y] = level.field[x][y];
1904 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1905 ChangeDelay[x][y] = 0;
1906 ChangePage[x][y] = -1;
1907 #if USE_NEW_CUSTOM_VALUE
1908 CustomValue[x][y] = 0; /* initialized in InitField() */
1910 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1912 WasJustMoving[x][y] = 0;
1913 WasJustFalling[x][y] = 0;
1914 CheckCollision[x][y] = 0;
1916 Pushed[x][y] = FALSE;
1918 ChangeCount[x][y] = 0;
1919 ChangeEvent[x][y] = -1;
1921 ExplodePhase[x][y] = 0;
1922 ExplodeDelay[x][y] = 0;
1923 ExplodeField[x][y] = EX_TYPE_NONE;
1925 RunnerVisit[x][y] = 0;
1926 PlayerVisit[x][y] = 0;
1929 GfxRandom[x][y] = INIT_GFX_RANDOM();
1930 GfxElement[x][y] = EL_UNDEFINED;
1931 GfxAction[x][y] = ACTION_DEFAULT;
1932 GfxDir[x][y] = MV_NONE;
1936 SCAN_PLAYFIELD(x, y)
1938 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1941 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1943 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1945 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1948 InitField(x, y, TRUE);
1953 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1954 emulate_sb ? EMU_SOKOBAN :
1955 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1957 #if USE_NEW_ALL_SLIPPERY
1958 /* initialize type of slippery elements */
1959 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1961 if (!IS_CUSTOM_ELEMENT(i))
1963 /* default: elements slip down either to the left or right randomly */
1964 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1966 /* SP style elements prefer to slip down on the left side */
1967 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1968 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1970 /* BD style elements prefer to slip down on the left side */
1971 if (game.emulation == EMU_BOULDERDASH)
1972 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1977 /* initialize explosion and ignition delay */
1978 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1980 if (!IS_CUSTOM_ELEMENT(i))
1983 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1984 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1985 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1986 int last_phase = (num_phase + 1) * delay;
1987 int half_phase = (num_phase / 2) * delay;
1989 element_info[i].explosion_delay = last_phase - 1;
1990 element_info[i].ignition_delay = half_phase;
1992 if (i == EL_BLACK_ORB)
1993 element_info[i].ignition_delay = 1;
1997 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1998 element_info[i].explosion_delay = 1;
2000 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2001 element_info[i].ignition_delay = 1;
2005 /* correct non-moving belts to start moving left */
2006 for (i = 0; i < NUM_BELTS; i++)
2007 if (game.belt_dir[i] == MV_NONE)
2008 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2010 /* check if any connected player was not found in playfield */
2011 for (i = 0; i < MAX_PLAYERS; i++)
2013 struct PlayerInfo *player = &stored_player[i];
2015 if (player->connected && !player->present)
2017 for (j = 0; j < MAX_PLAYERS; j++)
2019 struct PlayerInfo *some_player = &stored_player[j];
2020 int jx = some_player->jx, jy = some_player->jy;
2022 /* assign first free player found that is present in the playfield */
2023 if (some_player->present && !some_player->connected)
2025 player->present = TRUE;
2026 player->active = TRUE;
2028 some_player->present = FALSE;
2029 some_player->active = FALSE;
2032 player->element_nr = some_player->element_nr;
2035 player->artwork_element = some_player->artwork_element;
2037 player->block_last_field = some_player->block_last_field;
2038 player->block_delay_adjustment = some_player->block_delay_adjustment;
2040 StorePlayer[jx][jy] = player->element_nr;
2041 player->jx = player->last_jx = jx;
2042 player->jy = player->last_jy = jy;
2052 /* when playing a tape, eliminate all players which do not participate */
2054 for (i = 0; i < MAX_PLAYERS; i++)
2056 if (stored_player[i].active && !tape.player_participates[i])
2058 struct PlayerInfo *player = &stored_player[i];
2059 int jx = player->jx, jy = player->jy;
2061 player->active = FALSE;
2062 StorePlayer[jx][jy] = 0;
2063 Feld[jx][jy] = EL_EMPTY;
2067 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2069 /* when in single player mode, eliminate all but the first active player */
2071 for (i = 0; i < MAX_PLAYERS; i++)
2073 if (stored_player[i].active)
2075 for (j = i + 1; j < MAX_PLAYERS; j++)
2077 if (stored_player[j].active)
2079 struct PlayerInfo *player = &stored_player[j];
2080 int jx = player->jx, jy = player->jy;
2082 player->active = FALSE;
2083 player->present = FALSE;
2085 StorePlayer[jx][jy] = 0;
2086 Feld[jx][jy] = EL_EMPTY;
2093 /* when recording the game, store which players take part in the game */
2096 for (i = 0; i < MAX_PLAYERS; i++)
2097 if (stored_player[i].active)
2098 tape.player_participates[i] = TRUE;
2103 for (i = 0; i < MAX_PLAYERS; i++)
2105 struct PlayerInfo *player = &stored_player[i];
2107 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2112 if (local_player == player)
2113 printf("Player %d is local player.\n", i+1);
2117 if (BorderElement == EL_EMPTY)
2120 SBX_Right = lev_fieldx - SCR_FIELDX;
2122 SBY_Lower = lev_fieldy - SCR_FIELDY;
2127 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2129 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2132 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2133 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2135 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2136 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2138 /* if local player not found, look for custom element that might create
2139 the player (make some assumptions about the right custom element) */
2140 if (!local_player->present)
2142 int start_x = 0, start_y = 0;
2143 int found_rating = 0;
2144 int found_element = EL_UNDEFINED;
2145 int player_nr = local_player->index_nr;
2148 SCAN_PLAYFIELD(x, y)
2150 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2153 int element = Feld[x][y];
2158 if (level.use_start_element[player_nr] &&
2159 level.start_element[player_nr] == element &&
2166 found_element = element;
2169 if (!IS_CUSTOM_ELEMENT(element))
2172 if (CAN_CHANGE(element))
2174 for (i = 0; i < element_info[element].num_change_pages; i++)
2176 /* check for player created from custom element as single target */
2177 content = element_info[element].change_page[i].target_element;
2178 is_player = ELEM_IS_PLAYER(content);
2180 if (is_player && (found_rating < 3 || element < found_element))
2186 found_element = element;
2191 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2193 /* check for player created from custom element as explosion content */
2194 content = element_info[element].content.e[xx][yy];
2195 is_player = ELEM_IS_PLAYER(content);
2197 if (is_player && (found_rating < 2 || element < found_element))
2199 start_x = x + xx - 1;
2200 start_y = y + yy - 1;
2203 found_element = element;
2206 if (!CAN_CHANGE(element))
2209 for (i = 0; i < element_info[element].num_change_pages; i++)
2211 /* check for player created from custom element as extended target */
2213 element_info[element].change_page[i].target_content.e[xx][yy];
2215 is_player = ELEM_IS_PLAYER(content);
2217 if (is_player && (found_rating < 1 || element < found_element))
2219 start_x = x + xx - 1;
2220 start_y = y + yy - 1;
2223 found_element = element;
2229 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2230 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2233 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2234 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2239 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2240 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2241 local_player->jx - MIDPOSX);
2243 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2244 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2245 local_player->jy - MIDPOSY);
2248 if (!game.restart_level)
2249 CloseDoor(DOOR_CLOSE_1);
2251 /* !!! FIX THIS (START) !!! */
2252 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254 InitGameEngine_EM();
2261 /* after drawing the level, correct some elements */
2262 if (game.timegate_time_left == 0)
2263 CloseAllOpenTimegates();
2265 if (setup.soft_scrolling)
2266 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2268 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2271 /* !!! FIX THIS (END) !!! */
2273 if (!game.restart_level)
2275 /* copy default game door content to main double buffer */
2276 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2277 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2280 DrawGameDoorValues();
2282 if (!game.restart_level)
2286 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2287 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2288 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2292 /* copy actual game door content to door double buffer for OpenDoor() */
2293 BlitBitmap(drawto, bitmap_db_door,
2294 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2296 OpenDoor(DOOR_OPEN_ALL);
2298 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2300 if (setup.sound_music)
2303 KeyboardAutoRepeatOffUnlessAutoplay();
2307 for (i = 0; i < MAX_PLAYERS; i++)
2308 printf("Player %d %sactive.\n",
2309 i + 1, (stored_player[i].active ? "" : "not "));
2313 game.restart_level = FALSE;
2316 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2318 /* this is used for non-R'n'D game engines to update certain engine values */
2320 /* needed to determine if sounds are played within the visible screen area */
2321 scroll_x = actual_scroll_x;
2322 scroll_y = actual_scroll_y;
2325 void InitMovDir(int x, int y)
2327 int i, element = Feld[x][y];
2328 static int xy[4][2] =
2335 static int direction[3][4] =
2337 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2338 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2339 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2348 Feld[x][y] = EL_BUG;
2349 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2352 case EL_SPACESHIP_RIGHT:
2353 case EL_SPACESHIP_UP:
2354 case EL_SPACESHIP_LEFT:
2355 case EL_SPACESHIP_DOWN:
2356 Feld[x][y] = EL_SPACESHIP;
2357 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2360 case EL_BD_BUTTERFLY_RIGHT:
2361 case EL_BD_BUTTERFLY_UP:
2362 case EL_BD_BUTTERFLY_LEFT:
2363 case EL_BD_BUTTERFLY_DOWN:
2364 Feld[x][y] = EL_BD_BUTTERFLY;
2365 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2368 case EL_BD_FIREFLY_RIGHT:
2369 case EL_BD_FIREFLY_UP:
2370 case EL_BD_FIREFLY_LEFT:
2371 case EL_BD_FIREFLY_DOWN:
2372 Feld[x][y] = EL_BD_FIREFLY;
2373 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2376 case EL_PACMAN_RIGHT:
2378 case EL_PACMAN_LEFT:
2379 case EL_PACMAN_DOWN:
2380 Feld[x][y] = EL_PACMAN;
2381 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2384 case EL_SP_SNIKSNAK:
2385 MovDir[x][y] = MV_UP;
2388 case EL_SP_ELECTRON:
2389 MovDir[x][y] = MV_LEFT;
2396 Feld[x][y] = EL_MOLE;
2397 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2401 if (IS_CUSTOM_ELEMENT(element))
2403 struct ElementInfo *ei = &element_info[element];
2404 int move_direction_initial = ei->move_direction_initial;
2405 int move_pattern = ei->move_pattern;
2407 if (move_direction_initial == MV_START_PREVIOUS)
2409 if (MovDir[x][y] != MV_NONE)
2412 move_direction_initial = MV_START_AUTOMATIC;
2415 if (move_direction_initial == MV_START_RANDOM)
2416 MovDir[x][y] = 1 << RND(4);
2417 else if (move_direction_initial & MV_ANY_DIRECTION)
2418 MovDir[x][y] = move_direction_initial;
2419 else if (move_pattern == MV_ALL_DIRECTIONS ||
2420 move_pattern == MV_TURNING_LEFT ||
2421 move_pattern == MV_TURNING_RIGHT ||
2422 move_pattern == MV_TURNING_LEFT_RIGHT ||
2423 move_pattern == MV_TURNING_RIGHT_LEFT ||
2424 move_pattern == MV_TURNING_RANDOM)
2425 MovDir[x][y] = 1 << RND(4);
2426 else if (move_pattern == MV_HORIZONTAL)
2427 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2428 else if (move_pattern == MV_VERTICAL)
2429 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2430 else if (move_pattern & MV_ANY_DIRECTION)
2431 MovDir[x][y] = element_info[element].move_pattern;
2432 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2433 move_pattern == MV_ALONG_RIGHT_SIDE)
2435 /* use random direction as default start direction */
2436 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2437 MovDir[x][y] = 1 << RND(4);
2439 for (i = 0; i < NUM_DIRECTIONS; i++)
2441 int x1 = x + xy[i][0];
2442 int y1 = y + xy[i][1];
2444 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2446 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2447 MovDir[x][y] = direction[0][i];
2449 MovDir[x][y] = direction[1][i];
2458 MovDir[x][y] = 1 << RND(4);
2460 if (element != EL_BUG &&
2461 element != EL_SPACESHIP &&
2462 element != EL_BD_BUTTERFLY &&
2463 element != EL_BD_FIREFLY)
2466 for (i = 0; i < NUM_DIRECTIONS; i++)
2468 int x1 = x + xy[i][0];
2469 int y1 = y + xy[i][1];
2471 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2473 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2475 MovDir[x][y] = direction[0][i];
2478 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2479 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2481 MovDir[x][y] = direction[1][i];
2490 GfxDir[x][y] = MovDir[x][y];
2493 void InitAmoebaNr(int x, int y)
2496 int group_nr = AmoebeNachbarNr(x, y);
2500 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2502 if (AmoebaCnt[i] == 0)
2510 AmoebaNr[x][y] = group_nr;
2511 AmoebaCnt[group_nr]++;
2512 AmoebaCnt2[group_nr]++;
2518 boolean raise_level = FALSE;
2520 if (local_player->MovPos)
2523 if (tape.auto_play) /* tape might already be stopped here */
2524 tape.auto_play_level_solved = TRUE;
2526 local_player->LevelSolved = FALSE;
2528 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2532 if (!tape.playing && setup.sound_loops)
2533 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2534 SND_CTRL_PLAY_LOOP);
2536 while (TimeLeft > 0)
2538 if (!tape.playing && !setup.sound_loops)
2539 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2541 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2544 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2549 RaiseScore(level.score[SC_TIME_BONUS]);
2552 DrawGameValue_Time(TimeLeft);
2560 if (!tape.playing && setup.sound_loops)
2561 StopSound(SND_GAME_LEVELTIME_BONUS);
2563 else if (level.time == 0) /* level without time limit */
2565 if (!tape.playing && setup.sound_loops)
2566 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2567 SND_CTRL_PLAY_LOOP);
2569 while (TimePlayed < 999)
2571 if (!tape.playing && !setup.sound_loops)
2572 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2574 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2577 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2582 RaiseScore(level.score[SC_TIME_BONUS]);
2585 DrawGameValue_Time(TimePlayed);
2593 if (!tape.playing && setup.sound_loops)
2594 StopSound(SND_GAME_LEVELTIME_BONUS);
2597 /* close exit door after last player */
2598 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2599 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2600 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2602 int element = Feld[ExitX][ExitY];
2604 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2605 EL_SP_EXIT_CLOSING);
2607 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2610 /* player disappears */
2611 if (ExitX >= 0 && ExitY >= 0)
2612 DrawLevelField(ExitX, ExitY);
2619 CloseDoor(DOOR_CLOSE_1);
2624 SaveTape(tape.level_nr); /* Ask to save tape */
2627 if (level_nr == leveldir_current->handicap_level)
2629 leveldir_current->handicap_level++;
2630 SaveLevelSetup_SeriesInfo();
2633 if (level_editor_test_game)
2634 local_player->score = -1; /* no highscore when playing from editor */
2635 else if (level_nr < leveldir_current->last_level)
2636 raise_level = TRUE; /* advance to next level */
2638 if ((hi_pos = NewHiScore()) >= 0)
2640 game_status = GAME_MODE_SCORES;
2641 DrawHallOfFame(hi_pos);
2650 game_status = GAME_MODE_MAIN;
2667 LoadScore(level_nr);
2669 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2670 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2673 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2675 if (local_player->score > highscore[k].Score)
2677 /* player has made it to the hall of fame */
2679 if (k < MAX_SCORE_ENTRIES - 1)
2681 int m = MAX_SCORE_ENTRIES - 1;
2684 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2685 if (!strcmp(setup.player_name, highscore[l].Name))
2687 if (m == k) /* player's new highscore overwrites his old one */
2691 for (l = m; l > k; l--)
2693 strcpy(highscore[l].Name, highscore[l - 1].Name);
2694 highscore[l].Score = highscore[l - 1].Score;
2701 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2702 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2703 highscore[k].Score = local_player->score;
2709 else if (!strncmp(setup.player_name, highscore[k].Name,
2710 MAX_PLAYER_NAME_LEN))
2711 break; /* player already there with a higher score */
2717 SaveScore(level_nr);
2722 inline static int getElementMoveStepsize(int x, int y)
2724 int element = Feld[x][y];
2725 int direction = MovDir[x][y];
2726 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2727 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2728 int horiz_move = (dx != 0);
2729 int sign = (horiz_move ? dx : dy);
2730 int step = sign * element_info[element].move_stepsize;
2732 /* special values for move stepsize for spring and things on conveyor belt */
2736 if (element == EL_SPRING)
2737 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2738 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2739 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2740 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2742 if (CAN_FALL(element) &&
2743 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2744 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2745 else if (element == EL_SPRING)
2746 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2753 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2755 if (player->GfxAction != action || player->GfxDir != dir)
2758 printf("Player frame reset! (%d => %d, %d => %d)\n",
2759 player->GfxAction, action, player->GfxDir, dir);
2762 player->GfxAction = action;
2763 player->GfxDir = dir;
2765 player->StepFrame = 0;
2769 static void ResetRandomAnimationValue(int x, int y)
2771 GfxRandom[x][y] = INIT_GFX_RANDOM();
2774 static void ResetGfxAnimation(int x, int y)
2777 GfxAction[x][y] = ACTION_DEFAULT;
2778 GfxDir[x][y] = MovDir[x][y];
2781 void InitMovingField(int x, int y, int direction)
2783 int element = Feld[x][y];
2784 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2785 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2789 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2790 ResetGfxAnimation(x, y);
2792 MovDir[x][y] = direction;
2793 GfxDir[x][y] = direction;
2794 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2795 ACTION_FALLING : ACTION_MOVING);
2797 /* this is needed for CEs with property "can move" / "not moving" */
2799 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2801 if (Feld[newx][newy] == EL_EMPTY)
2802 Feld[newx][newy] = EL_BLOCKED;
2804 MovDir[newx][newy] = MovDir[x][y];
2806 #if USE_NEW_CUSTOM_VALUE
2807 CustomValue[newx][newy] = CustomValue[x][y];
2810 GfxFrame[newx][newy] = GfxFrame[x][y];
2811 GfxRandom[newx][newy] = GfxRandom[x][y];
2812 GfxAction[newx][newy] = GfxAction[x][y];
2813 GfxDir[newx][newy] = GfxDir[x][y];
2817 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2819 int direction = MovDir[x][y];
2820 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2821 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2827 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2829 int oldx = x, oldy = y;
2830 int direction = MovDir[x][y];
2832 if (direction == MV_LEFT)
2834 else if (direction == MV_RIGHT)
2836 else if (direction == MV_UP)
2838 else if (direction == MV_DOWN)
2841 *comes_from_x = oldx;
2842 *comes_from_y = oldy;
2845 int MovingOrBlocked2Element(int x, int y)
2847 int element = Feld[x][y];
2849 if (element == EL_BLOCKED)
2853 Blocked2Moving(x, y, &oldx, &oldy);
2854 return Feld[oldx][oldy];
2860 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2862 /* like MovingOrBlocked2Element(), but if element is moving
2863 and (x,y) is the field the moving element is just leaving,
2864 return EL_BLOCKED instead of the element value */
2865 int element = Feld[x][y];
2867 if (IS_MOVING(x, y))
2869 if (element == EL_BLOCKED)
2873 Blocked2Moving(x, y, &oldx, &oldy);
2874 return Feld[oldx][oldy];
2883 static void RemoveField(int x, int y)
2885 Feld[x][y] = EL_EMPTY;
2891 #if USE_NEW_CUSTOM_VALUE
2892 CustomValue[x][y] = 0;
2896 ChangeDelay[x][y] = 0;
2897 ChangePage[x][y] = -1;
2898 Pushed[x][y] = FALSE;
2901 ExplodeField[x][y] = EX_TYPE_NONE;
2904 GfxElement[x][y] = EL_UNDEFINED;
2905 GfxAction[x][y] = ACTION_DEFAULT;
2906 GfxDir[x][y] = MV_NONE;
2909 void RemoveMovingField(int x, int y)
2911 int oldx = x, oldy = y, newx = x, newy = y;
2912 int element = Feld[x][y];
2913 int next_element = EL_UNDEFINED;
2915 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2918 if (IS_MOVING(x, y))
2920 Moving2Blocked(x, y, &newx, &newy);
2922 if (Feld[newx][newy] != EL_BLOCKED)
2924 /* element is moving, but target field is not free (blocked), but
2925 already occupied by something different (example: acid pool);
2926 in this case, only remove the moving field, but not the target */
2928 RemoveField(oldx, oldy);
2930 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2932 DrawLevelField(oldx, oldy);
2937 else if (element == EL_BLOCKED)
2939 Blocked2Moving(x, y, &oldx, &oldy);
2940 if (!IS_MOVING(oldx, oldy))
2944 if (element == EL_BLOCKED &&
2945 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2946 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2947 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2948 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2949 next_element = get_next_element(Feld[oldx][oldy]);
2951 RemoveField(oldx, oldy);
2952 RemoveField(newx, newy);
2954 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2956 if (next_element != EL_UNDEFINED)
2957 Feld[oldx][oldy] = next_element;
2959 DrawLevelField(oldx, oldy);
2960 DrawLevelField(newx, newy);
2963 void DrawDynamite(int x, int y)
2965 int sx = SCREENX(x), sy = SCREENY(y);
2966 int graphic = el2img(Feld[x][y]);
2969 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2972 if (IS_WALKABLE_INSIDE(Back[x][y]))
2976 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2977 else if (Store[x][y])
2978 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2980 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2982 if (Back[x][y] || Store[x][y])
2983 DrawGraphicThruMask(sx, sy, graphic, frame);
2985 DrawGraphic(sx, sy, graphic, frame);
2988 void CheckDynamite(int x, int y)
2990 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2994 if (MovDelay[x][y] != 0)
2997 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3003 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3008 void DrawRelocatePlayer(struct PlayerInfo *player)
3010 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3011 boolean no_delay = (tape.warp_forward);
3012 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3013 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3014 int jx = player->jx;
3015 int jy = player->jy;
3017 if (level.instant_relocation)
3019 int offset = (setup.scroll_delay ? 3 : 0);
3021 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3023 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3024 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3025 local_player->jx - MIDPOSX);
3027 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3028 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3029 local_player->jy - MIDPOSY);
3033 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3034 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3035 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3037 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3038 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3039 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3041 /* don't scroll over playfield boundaries */
3042 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3043 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3045 /* don't scroll over playfield boundaries */
3046 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3047 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3050 RedrawPlayfield(TRUE, 0,0,0,0);
3054 int scroll_xx = -999, scroll_yy = -999;
3056 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3058 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3061 int fx = FX, fy = FY;
3063 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3064 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3065 local_player->jx - MIDPOSX);
3067 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3068 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3069 local_player->jy - MIDPOSY);
3071 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3072 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3074 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3080 fx += dx * TILEX / 2;
3081 fy += dy * TILEY / 2;
3083 ScrollLevel(dx, dy);
3086 /* scroll in two steps of half tile size to make things smoother */
3087 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3089 Delay(wait_delay_value);
3091 /* scroll second step to align at full tile size */
3093 Delay(wait_delay_value);
3098 Delay(wait_delay_value);
3102 void RelocatePlayer(int jx, int jy, int el_player_raw)
3104 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3105 int player_nr = GET_PLAYER_NR(el_player);
3106 struct PlayerInfo *player = &stored_player[player_nr];
3107 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3108 boolean no_delay = (tape.warp_forward);
3109 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3110 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3111 int old_jx = player->jx;
3112 int old_jy = player->jy;
3113 int old_element = Feld[old_jx][old_jy];
3114 int element = Feld[jx][jy];
3115 boolean player_relocated = (old_jx != jx || old_jy != jy);
3117 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3118 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3119 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3120 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3121 int leave_side_horiz = move_dir_horiz;
3122 int leave_side_vert = move_dir_vert;
3123 int enter_side = enter_side_horiz | enter_side_vert;
3124 int leave_side = leave_side_horiz | leave_side_vert;
3126 if (player->GameOver) /* do not reanimate dead player */
3129 if (!player_relocated) /* no need to relocate the player */
3132 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3134 RemoveField(jx, jy); /* temporarily remove newly placed player */
3135 DrawLevelField(jx, jy);
3138 if (player->present)
3140 while (player->MovPos)
3142 ScrollPlayer(player, SCROLL_GO_ON);
3143 ScrollScreen(NULL, SCROLL_GO_ON);
3145 AdvanceFrameAndPlayerCounters(player->index_nr);
3150 Delay(wait_delay_value);
3153 DrawPlayer(player); /* needed here only to cleanup last field */
3154 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3156 player->is_moving = FALSE;
3159 if (IS_CUSTOM_ELEMENT(old_element))
3160 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3162 player->index_bit, leave_side);
3164 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3166 player->index_bit, leave_side);
3168 Feld[jx][jy] = el_player;
3169 InitPlayerField(jx, jy, el_player, TRUE);
3171 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3173 Feld[jx][jy] = element;
3174 InitField(jx, jy, FALSE);
3177 if (player == local_player) /* only visually relocate local player */
3178 DrawRelocatePlayer(player);
3180 TestIfPlayerTouchesBadThing(jx, jy);
3181 TestIfPlayerTouchesCustomElement(jx, jy);
3183 if (IS_CUSTOM_ELEMENT(element))
3184 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3185 player->index_bit, enter_side);
3187 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3188 player->index_bit, enter_side);
3191 void Explode(int ex, int ey, int phase, int mode)
3197 /* !!! eliminate this variable !!! */
3198 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3200 if (game.explosions_delayed)
3202 ExplodeField[ex][ey] = mode;
3206 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3208 int center_element = Feld[ex][ey];
3209 int artwork_element, explosion_element; /* set these values later */
3212 /* --- This is only really needed (and now handled) in "Impact()". --- */
3213 /* do not explode moving elements that left the explode field in time */
3214 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3215 center_element == EL_EMPTY &&
3216 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3221 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3222 if (mode == EX_TYPE_NORMAL ||
3223 mode == EX_TYPE_CENTER ||
3224 mode == EX_TYPE_CROSS)
3225 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3228 /* remove things displayed in background while burning dynamite */
3229 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3232 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3234 /* put moving element to center field (and let it explode there) */
3235 center_element = MovingOrBlocked2Element(ex, ey);
3236 RemoveMovingField(ex, ey);
3237 Feld[ex][ey] = center_element;
3240 /* now "center_element" is finally determined -- set related values now */
3241 artwork_element = center_element; /* for custom player artwork */
3242 explosion_element = center_element; /* for custom player artwork */
3244 if (IS_PLAYER(ex, ey))
3246 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3248 artwork_element = stored_player[player_nr].artwork_element;
3250 if (level.use_explosion_element[player_nr])
3252 explosion_element = level.explosion_element[player_nr];
3253 artwork_element = explosion_element;
3258 if (mode == EX_TYPE_NORMAL ||
3259 mode == EX_TYPE_CENTER ||
3260 mode == EX_TYPE_CROSS)
3261 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3265 last_phase = element_info[explosion_element].explosion_delay + 1;
3267 last_phase = element_info[center_element].explosion_delay + 1;
3270 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3272 int xx = x - ex + 1;
3273 int yy = y - ey + 1;
3276 if (!IN_LEV_FIELD(x, y) ||
3277 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3278 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3281 element = Feld[x][y];
3283 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3285 element = MovingOrBlocked2Element(x, y);
3287 if (!IS_EXPLOSION_PROOF(element))
3288 RemoveMovingField(x, y);
3291 /* indestructible elements can only explode in center (but not flames) */
3292 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3293 mode == EX_TYPE_BORDER)) ||
3294 element == EL_FLAMES)
3297 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3298 behaviour, for example when touching a yamyam that explodes to rocks
3299 with active deadly shield, a rock is created under the player !!! */
3300 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3302 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3303 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3304 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3306 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3309 if (IS_ACTIVE_BOMB(element))
3311 /* re-activate things under the bomb like gate or penguin */
3312 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3319 /* save walkable background elements while explosion on same tile */
3320 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3321 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3322 Back[x][y] = element;
3324 /* ignite explodable elements reached by other explosion */
3325 if (element == EL_EXPLOSION)
3326 element = Store2[x][y];
3328 if (AmoebaNr[x][y] &&
3329 (element == EL_AMOEBA_FULL ||
3330 element == EL_BD_AMOEBA ||
3331 element == EL_AMOEBA_GROWING))
3333 AmoebaCnt[AmoebaNr[x][y]]--;
3334 AmoebaCnt2[AmoebaNr[x][y]]--;
3339 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3342 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3344 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3346 switch(StorePlayer[ex][ey])
3349 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3352 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3355 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3359 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3364 if (PLAYERINFO(ex, ey)->use_murphy)
3365 Store[x][y] = EL_EMPTY;
3368 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3369 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3370 else if (ELEM_IS_PLAYER(center_element))
3371 Store[x][y] = EL_EMPTY;
3372 else if (center_element == EL_YAMYAM)
3373 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3374 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3375 Store[x][y] = element_info[center_element].content.e[xx][yy];
3377 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3378 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3379 otherwise) -- FIX THIS !!! */
3380 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3381 Store[x][y] = element_info[element].content.e[1][1];
3383 else if (!CAN_EXPLODE(element))
3384 Store[x][y] = element_info[element].content.e[1][1];
3387 Store[x][y] = EL_EMPTY;
3389 else if (center_element == EL_MOLE)
3390 Store[x][y] = EL_EMERALD_RED;
3391 else if (center_element == EL_PENGUIN)
3392 Store[x][y] = EL_EMERALD_PURPLE;
3393 else if (center_element == EL_BUG)
3394 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3395 else if (center_element == EL_BD_BUTTERFLY)
3396 Store[x][y] = EL_BD_DIAMOND;
3397 else if (center_element == EL_SP_ELECTRON)
3398 Store[x][y] = EL_SP_INFOTRON;
3399 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3400 Store[x][y] = level.amoeba_content;
3401 else if (center_element == EL_YAMYAM)
3402 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3403 else if (IS_CUSTOM_ELEMENT(center_element) &&
3404 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3405 Store[x][y] = element_info[center_element].content.e[xx][yy];
3406 else if (element == EL_WALL_EMERALD)
3407 Store[x][y] = EL_EMERALD;
3408 else if (element == EL_WALL_DIAMOND)
3409 Store[x][y] = EL_DIAMOND;
3410 else if (element == EL_WALL_BD_DIAMOND)
3411 Store[x][y] = EL_BD_DIAMOND;
3412 else if (element == EL_WALL_EMERALD_YELLOW)
3413 Store[x][y] = EL_EMERALD_YELLOW;
3414 else if (element == EL_WALL_EMERALD_RED)
3415 Store[x][y] = EL_EMERALD_RED;
3416 else if (element == EL_WALL_EMERALD_PURPLE)
3417 Store[x][y] = EL_EMERALD_PURPLE;
3418 else if (element == EL_WALL_PEARL)
3419 Store[x][y] = EL_PEARL;
3420 else if (element == EL_WALL_CRYSTAL)
3421 Store[x][y] = EL_CRYSTAL;
3422 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3423 Store[x][y] = element_info[element].content.e[1][1];
3425 Store[x][y] = EL_EMPTY;
3428 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3429 center_element == EL_AMOEBA_TO_DIAMOND)
3430 Store2[x][y] = element;
3432 Feld[x][y] = EL_EXPLOSION;
3433 GfxElement[x][y] = artwork_element;
3435 ExplodePhase[x][y] = 1;
3436 ExplodeDelay[x][y] = last_phase;
3441 if (center_element == EL_YAMYAM)
3442 game.yamyam_content_nr =
3443 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3455 GfxFrame[x][y] = 0; /* restart explosion animation */
3457 last_phase = ExplodeDelay[x][y];
3459 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3463 /* activate this even in non-DEBUG version until cause for crash in
3464 getGraphicAnimationFrame() (see below) is found and eliminated */
3469 if (GfxElement[x][y] == EL_UNDEFINED)
3472 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3473 printf("Explode(): This should never happen!\n");
3476 GfxElement[x][y] = EL_EMPTY;
3480 border_element = Store2[x][y];
3481 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3482 border_element = StorePlayer[x][y];
3484 if (phase == element_info[border_element].ignition_delay ||
3485 phase == last_phase)
3487 boolean border_explosion = FALSE;
3489 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3490 !PLAYER_EXPLOSION_PROTECTED(x, y))
3492 KillPlayerUnlessExplosionProtected(x, y);
3493 border_explosion = TRUE;
3495 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3497 Feld[x][y] = Store2[x][y];
3500 border_explosion = TRUE;
3502 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3504 AmoebeUmwandeln(x, y);
3506 border_explosion = TRUE;
3509 /* if an element just explodes due to another explosion (chain-reaction),
3510 do not immediately end the new explosion when it was the last frame of
3511 the explosion (as it would be done in the following "if"-statement!) */
3512 if (border_explosion && phase == last_phase)
3516 if (phase == last_phase)
3520 element = Feld[x][y] = Store[x][y];
3521 Store[x][y] = Store2[x][y] = 0;
3522 GfxElement[x][y] = EL_UNDEFINED;
3524 /* player can escape from explosions and might therefore be still alive */
3525 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3526 element <= EL_PLAYER_IS_EXPLODING_4)
3528 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3529 int explosion_element = EL_PLAYER_1 + player_nr;
3530 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3531 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3533 if (level.use_explosion_element[player_nr])
3534 explosion_element = level.explosion_element[player_nr];
3536 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3537 element_info[explosion_element].content.e[xx][yy]);
3540 /* restore probably existing indestructible background element */
3541 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3542 element = Feld[x][y] = Back[x][y];
3545 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3546 GfxDir[x][y] = MV_NONE;
3547 ChangeDelay[x][y] = 0;
3548 ChangePage[x][y] = -1;
3550 #if USE_NEW_CUSTOM_VALUE
3551 CustomValue[x][y] = 0;
3554 InitField_WithBug2(x, y, FALSE);
3556 DrawLevelField(x, y);
3558 TestIfElementTouchesCustomElement(x, y);
3560 if (GFX_CRUMBLED(element))
3561 DrawLevelFieldCrumbledSandNeighbours(x, y);
3563 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3564 StorePlayer[x][y] = 0;
3566 if (ELEM_IS_PLAYER(element))
3567 RelocatePlayer(x, y, element);
3569 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3571 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3572 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3575 DrawLevelFieldCrumbledSand(x, y);
3577 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3579 DrawLevelElement(x, y, Back[x][y]);
3580 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3582 else if (IS_WALKABLE_UNDER(Back[x][y]))
3584 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3585 DrawLevelElementThruMask(x, y, Back[x][y]);
3587 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3588 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3592 void DynaExplode(int ex, int ey)
3595 int dynabomb_element = Feld[ex][ey];
3596 int dynabomb_size = 1;
3597 boolean dynabomb_xl = FALSE;
3598 struct PlayerInfo *player;
3599 static int xy[4][2] =
3607 if (IS_ACTIVE_BOMB(dynabomb_element))
3609 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3610 dynabomb_size = player->dynabomb_size;
3611 dynabomb_xl = player->dynabomb_xl;
3612 player->dynabombs_left++;
3615 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3617 for (i = 0; i < NUM_DIRECTIONS; i++)
3619 for (j = 1; j <= dynabomb_size; j++)
3621 int x = ex + j * xy[i][0];
3622 int y = ey + j * xy[i][1];
3625 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3628 element = Feld[x][y];
3630 /* do not restart explosions of fields with active bombs */
3631 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3634 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3636 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3637 !IS_DIGGABLE(element) && !dynabomb_xl)
3643 void Bang(int x, int y)
3645 int element = MovingOrBlocked2Element(x, y);
3646 int explosion_type = EX_TYPE_NORMAL;
3648 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3650 struct PlayerInfo *player = PLAYERINFO(x, y);
3652 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3653 player->element_nr);
3655 if (level.use_explosion_element[player->index_nr])
3657 int explosion_element = level.explosion_element[player->index_nr];
3659 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3660 explosion_type = EX_TYPE_CROSS;
3661 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3662 explosion_type = EX_TYPE_CENTER;
3670 case EL_BD_BUTTERFLY:
3673 case EL_DARK_YAMYAM:
3677 RaiseScoreElement(element);
3680 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3681 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3682 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3683 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3684 case EL_DYNABOMB_INCREASE_NUMBER:
3685 case EL_DYNABOMB_INCREASE_SIZE:
3686 case EL_DYNABOMB_INCREASE_POWER:
3687 explosion_type = EX_TYPE_DYNA;
3692 case EL_LAMP_ACTIVE:
3693 case EL_AMOEBA_TO_DIAMOND:
3694 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3695 explosion_type = EX_TYPE_CENTER;
3699 if (element_info[element].explosion_type == EXPLODES_CROSS)
3700 explosion_type = EX_TYPE_CROSS;
3701 else if (element_info[element].explosion_type == EXPLODES_1X1)
3702 explosion_type = EX_TYPE_CENTER;
3706 if (explosion_type == EX_TYPE_DYNA)
3709 Explode(x, y, EX_PHASE_START, explosion_type);
3711 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3714 void SplashAcid(int x, int y)
3716 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3717 (!IN_LEV_FIELD(x - 1, y - 2) ||
3718 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3719 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3721 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3722 (!IN_LEV_FIELD(x + 1, y - 2) ||
3723 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3724 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3726 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3729 static void InitBeltMovement()
3731 static int belt_base_element[4] =
3733 EL_CONVEYOR_BELT_1_LEFT,
3734 EL_CONVEYOR_BELT_2_LEFT,
3735 EL_CONVEYOR_BELT_3_LEFT,
3736 EL_CONVEYOR_BELT_4_LEFT
3738 static int belt_base_active_element[4] =
3740 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3741 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3742 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3743 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3748 /* set frame order for belt animation graphic according to belt direction */
3749 for (i = 0; i < NUM_BELTS; i++)
3753 for (j = 0; j < NUM_BELT_PARTS; j++)
3755 int element = belt_base_active_element[belt_nr] + j;
3756 int graphic = el2img(element);
3758 if (game.belt_dir[i] == MV_LEFT)
3759 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3761 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3766 SCAN_PLAYFIELD(x, y)
3768 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3771 int element = Feld[x][y];
3773 for (i = 0; i < NUM_BELTS; i++)
3775 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3777 int e_belt_nr = getBeltNrFromBeltElement(element);
3780 if (e_belt_nr == belt_nr)
3782 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3784 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3791 static void ToggleBeltSwitch(int x, int y)
3793 static int belt_base_element[4] =
3795 EL_CONVEYOR_BELT_1_LEFT,
3796 EL_CONVEYOR_BELT_2_LEFT,
3797 EL_CONVEYOR_BELT_3_LEFT,
3798 EL_CONVEYOR_BELT_4_LEFT
3800 static int belt_base_active_element[4] =
3802 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3803 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3804 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3805 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3807 static int belt_base_switch_element[4] =
3809 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3810 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3811 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3812 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3814 static int belt_move_dir[4] =
3822 int element = Feld[x][y];
3823 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3824 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3825 int belt_dir = belt_move_dir[belt_dir_nr];
3828 if (!IS_BELT_SWITCH(element))
3831 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3832 game.belt_dir[belt_nr] = belt_dir;
3834 if (belt_dir_nr == 3)
3837 /* set frame order for belt animation graphic according to belt direction */
3838 for (i = 0; i < NUM_BELT_PARTS; i++)
3840 int element = belt_base_active_element[belt_nr] + i;
3841 int graphic = el2img(element);
3843 if (belt_dir == MV_LEFT)
3844 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3846 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3850 SCAN_PLAYFIELD(xx, yy)
3852 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3855 int element = Feld[xx][yy];
3857 if (IS_BELT_SWITCH(element))
3859 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3861 if (e_belt_nr == belt_nr)
3863 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3864 DrawLevelField(xx, yy);
3867 else if (IS_BELT(element) && belt_dir != MV_NONE)
3869 int e_belt_nr = getBeltNrFromBeltElement(element);
3871 if (e_belt_nr == belt_nr)
3873 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3875 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3876 DrawLevelField(xx, yy);
3879 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3881 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3883 if (e_belt_nr == belt_nr)
3885 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3887 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3888 DrawLevelField(xx, yy);
3894 static void ToggleSwitchgateSwitch(int x, int y)
3898 game.switchgate_pos = !game.switchgate_pos;
3901 SCAN_PLAYFIELD(xx, yy)
3903 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3906 int element = Feld[xx][yy];
3908 if (element == EL_SWITCHGATE_SWITCH_UP ||
3909 element == EL_SWITCHGATE_SWITCH_DOWN)
3911 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3912 DrawLevelField(xx, yy);
3914 else if (element == EL_SWITCHGATE_OPEN ||
3915 element == EL_SWITCHGATE_OPENING)
3917 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3919 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3921 else if (element == EL_SWITCHGATE_CLOSED ||
3922 element == EL_SWITCHGATE_CLOSING)
3924 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3926 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3931 static int getInvisibleActiveFromInvisibleElement(int element)
3933 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3934 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3935 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3939 static int getInvisibleFromInvisibleActiveElement(int element)
3941 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3942 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3943 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3947 static void RedrawAllLightSwitchesAndInvisibleElements()
3952 SCAN_PLAYFIELD(x, y)
3954 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3957 int element = Feld[x][y];
3959 if (element == EL_LIGHT_SWITCH &&
3960 game.light_time_left > 0)
3962 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3963 DrawLevelField(x, y);
3965 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3966 game.light_time_left == 0)
3968 Feld[x][y] = EL_LIGHT_SWITCH;
3969 DrawLevelField(x, y);
3971 else if (element == EL_EMC_DRIPPER &&
3972 game.light_time_left > 0)
3974 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3975 DrawLevelField(x, y);
3977 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3978 game.light_time_left == 0)
3980 Feld[x][y] = EL_EMC_DRIPPER;
3981 DrawLevelField(x, y);
3983 else if (element == EL_INVISIBLE_STEELWALL ||
3984 element == EL_INVISIBLE_WALL ||
3985 element == EL_INVISIBLE_SAND)
3987 if (game.light_time_left > 0)
3988 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3990 DrawLevelField(x, y);
3992 /* uncrumble neighbour fields, if needed */
3993 if (element == EL_INVISIBLE_SAND)
3994 DrawLevelFieldCrumbledSandNeighbours(x, y);
3996 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3997 element == EL_INVISIBLE_WALL_ACTIVE ||
3998 element == EL_INVISIBLE_SAND_ACTIVE)
4000 if (game.light_time_left == 0)
4001 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4003 DrawLevelField(x, y);
4005 /* re-crumble neighbour fields, if needed */
4006 if (element == EL_INVISIBLE_SAND)
4007 DrawLevelFieldCrumbledSandNeighbours(x, y);
4012 static void RedrawAllInvisibleElementsForLenses()
4017 SCAN_PLAYFIELD(x, y)
4019 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4022 int element = Feld[x][y];
4024 if (element == EL_EMC_DRIPPER &&
4025 game.lenses_time_left > 0)
4027 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4028 DrawLevelField(x, y);
4030 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4031 game.lenses_time_left == 0)
4033 Feld[x][y] = EL_EMC_DRIPPER;
4034 DrawLevelField(x, y);
4036 else if (element == EL_INVISIBLE_STEELWALL ||
4037 element == EL_INVISIBLE_WALL ||
4038 element == EL_INVISIBLE_SAND)
4040 if (game.lenses_time_left > 0)
4041 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4043 DrawLevelField(x, y);
4045 /* uncrumble neighbour fields, if needed */
4046 if (element == EL_INVISIBLE_SAND)
4047 DrawLevelFieldCrumbledSandNeighbours(x, y);
4049 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4050 element == EL_INVISIBLE_WALL_ACTIVE ||
4051 element == EL_INVISIBLE_SAND_ACTIVE)
4053 if (game.lenses_time_left == 0)
4054 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4056 DrawLevelField(x, y);
4058 /* re-crumble neighbour fields, if needed */
4059 if (element == EL_INVISIBLE_SAND)
4060 DrawLevelFieldCrumbledSandNeighbours(x, y);
4065 static void RedrawAllInvisibleElementsForMagnifier()
4070 SCAN_PLAYFIELD(x, y)
4072 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4075 int element = Feld[x][y];
4077 if (element == EL_EMC_FAKE_GRASS &&
4078 game.magnify_time_left > 0)
4080 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4081 DrawLevelField(x, y);
4083 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4084 game.magnify_time_left == 0)
4086 Feld[x][y] = EL_EMC_FAKE_GRASS;
4087 DrawLevelField(x, y);
4089 else if (IS_GATE_GRAY(element) &&
4090 game.magnify_time_left > 0)
4092 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4093 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4094 IS_EM_GATE_GRAY(element) ?
4095 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4096 IS_EMC_GATE_GRAY(element) ?
4097 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4099 DrawLevelField(x, y);
4101 else if (IS_GATE_GRAY_ACTIVE(element) &&
4102 game.magnify_time_left == 0)
4104 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4105 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4106 IS_EM_GATE_GRAY_ACTIVE(element) ?
4107 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4108 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4109 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4111 DrawLevelField(x, y);
4116 static void ToggleLightSwitch(int x, int y)
4118 int element = Feld[x][y];
4120 game.light_time_left =
4121 (element == EL_LIGHT_SWITCH ?
4122 level.time_light * FRAMES_PER_SECOND : 0);
4124 RedrawAllLightSwitchesAndInvisibleElements();
4127 static void ActivateTimegateSwitch(int x, int y)
4131 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4134 SCAN_PLAYFIELD(xx, yy)
4136 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4139 int element = Feld[xx][yy];
4141 if (element == EL_TIMEGATE_CLOSED ||
4142 element == EL_TIMEGATE_CLOSING)
4144 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4145 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4149 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4151 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4152 DrawLevelField(xx, yy);
4158 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4161 void Impact(int x, int y)
4163 boolean last_line = (y == lev_fieldy - 1);
4164 boolean object_hit = FALSE;
4165 boolean impact = (last_line || object_hit);
4166 int element = Feld[x][y];
4167 int smashed = EL_STEELWALL;
4169 if (!last_line) /* check if element below was hit */
4171 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4174 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4175 MovDir[x][y + 1] != MV_DOWN ||
4176 MovPos[x][y + 1] <= TILEY / 2));
4178 /* do not smash moving elements that left the smashed field in time */
4179 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4180 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4183 #if USE_QUICKSAND_IMPACT_BUGFIX
4184 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4186 RemoveMovingField(x, y + 1);
4187 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4188 Feld[x][y + 2] = EL_ROCK;
4189 DrawLevelField(x, y + 2);
4196 smashed = MovingOrBlocked2Element(x, y + 1);
4198 impact = (last_line || object_hit);
4201 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4203 SplashAcid(x, y + 1);
4207 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4208 /* only reset graphic animation if graphic really changes after impact */
4210 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4212 ResetGfxAnimation(x, y);
4213 DrawLevelField(x, y);
4216 if (impact && CAN_EXPLODE_IMPACT(element))
4221 else if (impact && element == EL_PEARL)
4223 ResetGfxAnimation(x, y);
4225 Feld[x][y] = EL_PEARL_BREAKING;
4226 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4229 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4231 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4236 if (impact && element == EL_AMOEBA_DROP)
4238 if (object_hit && IS_PLAYER(x, y + 1))
4239 KillPlayerUnlessEnemyProtected(x, y + 1);
4240 else if (object_hit && smashed == EL_PENGUIN)
4244 Feld[x][y] = EL_AMOEBA_GROWING;
4245 Store[x][y] = EL_AMOEBA_WET;
4247 ResetRandomAnimationValue(x, y);
4252 if (object_hit) /* check which object was hit */
4254 if (CAN_PASS_MAGIC_WALL(element) &&
4255 (smashed == EL_MAGIC_WALL ||
4256 smashed == EL_BD_MAGIC_WALL))
4259 int activated_magic_wall =
4260 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4261 EL_BD_MAGIC_WALL_ACTIVE);
4263 /* activate magic wall / mill */
4265 SCAN_PLAYFIELD(xx, yy)
4267 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4269 if (Feld[xx][yy] == smashed)
4270 Feld[xx][yy] = activated_magic_wall;
4272 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4273 game.magic_wall_active = TRUE;
4275 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4276 SND_MAGIC_WALL_ACTIVATING :
4277 SND_BD_MAGIC_WALL_ACTIVATING));
4280 if (IS_PLAYER(x, y + 1))
4282 if (CAN_SMASH_PLAYER(element))
4284 KillPlayerUnlessEnemyProtected(x, y + 1);
4288 else if (smashed == EL_PENGUIN)
4290 if (CAN_SMASH_PLAYER(element))
4296 else if (element == EL_BD_DIAMOND)
4298 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4304 else if (((element == EL_SP_INFOTRON ||
4305 element == EL_SP_ZONK) &&
4306 (smashed == EL_SP_SNIKSNAK ||
4307 smashed == EL_SP_ELECTRON ||
4308 smashed == EL_SP_DISK_ORANGE)) ||
4309 (element == EL_SP_INFOTRON &&
4310 smashed == EL_SP_DISK_YELLOW))
4315 else if (CAN_SMASH_EVERYTHING(element))
4317 if (IS_CLASSIC_ENEMY(smashed) ||
4318 CAN_EXPLODE_SMASHED(smashed))
4323 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4325 if (smashed == EL_LAMP ||
4326 smashed == EL_LAMP_ACTIVE)
4331 else if (smashed == EL_NUT)
4333 Feld[x][y + 1] = EL_NUT_BREAKING;
4334 PlayLevelSound(x, y, SND_NUT_BREAKING);
4335 RaiseScoreElement(EL_NUT);
4338 else if (smashed == EL_PEARL)
4340 ResetGfxAnimation(x, y);
4342 Feld[x][y + 1] = EL_PEARL_BREAKING;
4343 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4346 else if (smashed == EL_DIAMOND)
4348 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4349 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4352 else if (IS_BELT_SWITCH(smashed))
4354 ToggleBeltSwitch(x, y + 1);
4356 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4357 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4359 ToggleSwitchgateSwitch(x, y + 1);
4361 else if (smashed == EL_LIGHT_SWITCH ||
4362 smashed == EL_LIGHT_SWITCH_ACTIVE)
4364 ToggleLightSwitch(x, y + 1);
4369 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4372 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4374 CheckElementChangeBySide(x, y + 1, smashed, element,
4375 CE_SWITCHED, CH_SIDE_TOP);
4376 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4382 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4387 /* play sound of magic wall / mill */
4389 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4390 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4392 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4393 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4394 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4395 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4400 /* play sound of object that hits the ground */
4401 if (last_line || object_hit)
4402 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4405 inline static void TurnRoundExt(int x, int y)
4417 { 0, 0 }, { 0, 0 }, { 0, 0 },
4422 int left, right, back;
4426 { MV_DOWN, MV_UP, MV_RIGHT },
4427 { MV_UP, MV_DOWN, MV_LEFT },
4429 { MV_LEFT, MV_RIGHT, MV_DOWN },
4433 { MV_RIGHT, MV_LEFT, MV_UP }
4436 int element = Feld[x][y];
4437 int move_pattern = element_info[element].move_pattern;
4439 int old_move_dir = MovDir[x][y];
4440 int left_dir = turn[old_move_dir].left;
4441 int right_dir = turn[old_move_dir].right;
4442 int back_dir = turn[old_move_dir].back;
4444 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4445 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4446 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4447 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4449 int left_x = x + left_dx, left_y = y + left_dy;
4450 int right_x = x + right_dx, right_y = y + right_dy;
4451 int move_x = x + move_dx, move_y = y + move_dy;
4455 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4457 TestIfBadThingTouchesOtherBadThing(x, y);
4459 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4460 MovDir[x][y] = right_dir;
4461 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4462 MovDir[x][y] = left_dir;
4464 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4466 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4469 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4471 TestIfBadThingTouchesOtherBadThing(x, y);
4473 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4474 MovDir[x][y] = left_dir;
4475 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4476 MovDir[x][y] = right_dir;
4478 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4480 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4483 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4485 TestIfBadThingTouchesOtherBadThing(x, y);
4487 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4488 MovDir[x][y] = left_dir;
4489 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4490 MovDir[x][y] = right_dir;
4492 if (MovDir[x][y] != old_move_dir)
4495 else if (element == EL_YAMYAM)
4497 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4498 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4500 if (can_turn_left && can_turn_right)
4501 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4502 else if (can_turn_left)
4503 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4504 else if (can_turn_right)
4505 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4507 MovDir[x][y] = back_dir;
4509 MovDelay[x][y] = 16 + 16 * RND(3);
4511 else if (element == EL_DARK_YAMYAM)
4513 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4515 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4518 if (can_turn_left && can_turn_right)
4519 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4520 else if (can_turn_left)
4521 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4522 else if (can_turn_right)
4523 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4525 MovDir[x][y] = back_dir;
4527 MovDelay[x][y] = 16 + 16 * RND(3);
4529 else if (element == EL_PACMAN)
4531 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4532 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4534 if (can_turn_left && can_turn_right)
4535 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4536 else if (can_turn_left)
4537 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4538 else if (can_turn_right)
4539 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4541 MovDir[x][y] = back_dir;
4543 MovDelay[x][y] = 6 + RND(40);
4545 else if (element == EL_PIG)
4547 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4548 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4549 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4550 boolean should_turn_left, should_turn_right, should_move_on;
4552 int rnd = RND(rnd_value);
4554 should_turn_left = (can_turn_left &&
4556 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4557 y + back_dy + left_dy)));
4558 should_turn_right = (can_turn_right &&
4560 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4561 y + back_dy + right_dy)));
4562 should_move_on = (can_move_on &&
4565 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4566 y + move_dy + left_dy) ||
4567 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4568 y + move_dy + right_dy)));
4570 if (should_turn_left || should_turn_right || should_move_on)
4572 if (should_turn_left && should_turn_right && should_move_on)
4573 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4574 rnd < 2 * rnd_value / 3 ? right_dir :
4576 else if (should_turn_left && should_turn_right)
4577 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4578 else if (should_turn_left && should_move_on)
4579 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4580 else if (should_turn_right && should_move_on)
4581 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4582 else if (should_turn_left)
4583 MovDir[x][y] = left_dir;
4584 else if (should_turn_right)
4585 MovDir[x][y] = right_dir;
4586 else if (should_move_on)
4587 MovDir[x][y] = old_move_dir;
4589 else if (can_move_on && rnd > rnd_value / 8)
4590 MovDir[x][y] = old_move_dir;
4591 else if (can_turn_left && can_turn_right)
4592 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4593 else if (can_turn_left && rnd > rnd_value / 8)
4594 MovDir[x][y] = left_dir;
4595 else if (can_turn_right && rnd > rnd_value/8)
4596 MovDir[x][y] = right_dir;
4598 MovDir[x][y] = back_dir;
4600 xx = x + move_xy[MovDir[x][y]].dx;
4601 yy = y + move_xy[MovDir[x][y]].dy;
4603 if (!IN_LEV_FIELD(xx, yy) ||
4604 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4605 MovDir[x][y] = old_move_dir;
4609 else if (element == EL_DRAGON)
4611 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4612 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4613 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4615 int rnd = RND(rnd_value);
4617 if (can_move_on && rnd > rnd_value / 8)
4618 MovDir[x][y] = old_move_dir;
4619 else if (can_turn_left && can_turn_right)
4620 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4621 else if (can_turn_left && rnd > rnd_value / 8)
4622 MovDir[x][y] = left_dir;
4623 else if (can_turn_right && rnd > rnd_value / 8)
4624 MovDir[x][y] = right_dir;
4626 MovDir[x][y] = back_dir;
4628 xx = x + move_xy[MovDir[x][y]].dx;
4629 yy = y + move_xy[MovDir[x][y]].dy;
4631 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4632 MovDir[x][y] = old_move_dir;
4636 else if (element == EL_MOLE)
4638 boolean can_move_on =
4639 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4640 IS_AMOEBOID(Feld[move_x][move_y]) ||
4641 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4644 boolean can_turn_left =
4645 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4646 IS_AMOEBOID(Feld[left_x][left_y])));
4648 boolean can_turn_right =
4649 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4650 IS_AMOEBOID(Feld[right_x][right_y])));
4652 if (can_turn_left && can_turn_right)
4653 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4654 else if (can_turn_left)
4655 MovDir[x][y] = left_dir;
4657 MovDir[x][y] = right_dir;
4660 if (MovDir[x][y] != old_move_dir)
4663 else if (element == EL_BALLOON)
4665 MovDir[x][y] = game.wind_direction;
4668 else if (element == EL_SPRING)
4670 #if USE_NEW_SPRING_BUMPER
4671 if (MovDir[x][y] & MV_HORIZONTAL)
4673 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4674 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4676 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4677 ResetGfxAnimation(move_x, move_y);
4678 DrawLevelField(move_x, move_y);
4680 MovDir[x][y] = back_dir;
4682 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4683 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4684 MovDir[x][y] = MV_NONE;
4687 if (MovDir[x][y] & MV_HORIZONTAL &&
4688 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4689 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4690 MovDir[x][y] = MV_NONE;
4695 else if (element == EL_ROBOT ||
4696 element == EL_SATELLITE ||
4697 element == EL_PENGUIN)
4699 int attr_x = -1, attr_y = -1;
4710 for (i = 0; i < MAX_PLAYERS; i++)
4712 struct PlayerInfo *player = &stored_player[i];
4713 int jx = player->jx, jy = player->jy;
4715 if (!player->active)
4719 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4727 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4728 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4729 game.engine_version < VERSION_IDENT(3,1,0,0)))
4735 if (element == EL_PENGUIN)
4738 static int xy[4][2] =
4746 for (i = 0; i < NUM_DIRECTIONS; i++)
4748 int ex = x + xy[i][0];
4749 int ey = y + xy[i][1];
4751 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4760 MovDir[x][y] = MV_NONE;
4762 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4763 else if (attr_x > x)
4764 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4766 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4767 else if (attr_y > y)
4768 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4770 if (element == EL_ROBOT)
4774 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4775 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4776 Moving2Blocked(x, y, &newx, &newy);
4778 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4779 MovDelay[x][y] = 8 + 8 * !RND(3);
4781 MovDelay[x][y] = 16;
4783 else if (element == EL_PENGUIN)
4789 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4791 boolean first_horiz = RND(2);
4792 int new_move_dir = MovDir[x][y];
4795 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4796 Moving2Blocked(x, y, &newx, &newy);
4798 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4802 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4803 Moving2Blocked(x, y, &newx, &newy);
4805 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4808 MovDir[x][y] = old_move_dir;
4812 else /* (element == EL_SATELLITE) */
4818 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4820 boolean first_horiz = RND(2);
4821 int new_move_dir = MovDir[x][y];
4824 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4825 Moving2Blocked(x, y, &newx, &newy);
4827 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4831 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4832 Moving2Blocked(x, y, &newx, &newy);
4834 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4837 MovDir[x][y] = old_move_dir;
4842 else if (move_pattern == MV_TURNING_LEFT ||
4843 move_pattern == MV_TURNING_RIGHT ||
4844 move_pattern == MV_TURNING_LEFT_RIGHT ||
4845 move_pattern == MV_TURNING_RIGHT_LEFT ||
4846 move_pattern == MV_TURNING_RANDOM ||
4847 move_pattern == MV_ALL_DIRECTIONS)
4849 boolean can_turn_left =
4850 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4851 boolean can_turn_right =
4852 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4854 if (element_info[element].move_stepsize == 0) /* "not moving" */
4857 if (move_pattern == MV_TURNING_LEFT)
4858 MovDir[x][y] = left_dir;
4859 else if (move_pattern == MV_TURNING_RIGHT)
4860 MovDir[x][y] = right_dir;
4861 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4862 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4863 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4864 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4865 else if (move_pattern == MV_TURNING_RANDOM)
4866 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4867 can_turn_right && !can_turn_left ? right_dir :
4868 RND(2) ? left_dir : right_dir);
4869 else if (can_turn_left && can_turn_right)
4870 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4871 else if (can_turn_left)
4872 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4873 else if (can_turn_right)
4874 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4876 MovDir[x][y] = back_dir;
4878 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4880 else if (move_pattern == MV_HORIZONTAL ||
4881 move_pattern == MV_VERTICAL)
4883 if (move_pattern & old_move_dir)
4884 MovDir[x][y] = back_dir;
4885 else if (move_pattern == MV_HORIZONTAL)
4886 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4887 else if (move_pattern == MV_VERTICAL)
4888 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4890 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4892 else if (move_pattern & MV_ANY_DIRECTION)
4894 MovDir[x][y] = move_pattern;
4895 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4897 else if (move_pattern & MV_WIND_DIRECTION)
4899 MovDir[x][y] = game.wind_direction;
4900 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4902 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4904 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4905 MovDir[x][y] = left_dir;
4906 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4907 MovDir[x][y] = right_dir;
4909 if (MovDir[x][y] != old_move_dir)
4910 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4912 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4914 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4915 MovDir[x][y] = right_dir;
4916 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4917 MovDir[x][y] = left_dir;
4919 if (MovDir[x][y] != old_move_dir)
4920 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4922 else if (move_pattern == MV_TOWARDS_PLAYER ||
4923 move_pattern == MV_AWAY_FROM_PLAYER)
4925 int attr_x = -1, attr_y = -1;
4927 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4938 for (i = 0; i < MAX_PLAYERS; i++)
4940 struct PlayerInfo *player = &stored_player[i];
4941 int jx = player->jx, jy = player->jy;
4943 if (!player->active)
4947 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4955 MovDir[x][y] = MV_NONE;
4957 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4958 else if (attr_x > x)
4959 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4961 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4962 else if (attr_y > y)
4963 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4965 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4967 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4969 boolean first_horiz = RND(2);
4970 int new_move_dir = MovDir[x][y];
4972 if (element_info[element].move_stepsize == 0) /* "not moving" */
4974 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4975 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4981 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4982 Moving2Blocked(x, y, &newx, &newy);
4984 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4988 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4989 Moving2Blocked(x, y, &newx, &newy);
4991 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4994 MovDir[x][y] = old_move_dir;
4997 else if (move_pattern == MV_WHEN_PUSHED ||
4998 move_pattern == MV_WHEN_DROPPED)
5000 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5001 MovDir[x][y] = MV_NONE;
5005 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5007 static int test_xy[7][2] =
5017 static int test_dir[7] =
5027 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5028 int move_preference = -1000000; /* start with very low preference */
5029 int new_move_dir = MV_NONE;
5030 int start_test = RND(4);
5033 for (i = 0; i < NUM_DIRECTIONS; i++)
5035 int move_dir = test_dir[start_test + i];
5036 int move_dir_preference;
5038 xx = x + test_xy[start_test + i][0];
5039 yy = y + test_xy[start_test + i][1];
5041 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5042 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5044 new_move_dir = move_dir;
5049 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5052 move_dir_preference = -1 * RunnerVisit[xx][yy];
5053 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5054 move_dir_preference = PlayerVisit[xx][yy];
5056 if (move_dir_preference > move_preference)
5058 /* prefer field that has not been visited for the longest time */
5059 move_preference = move_dir_preference;
5060 new_move_dir = move_dir;
5062 else if (move_dir_preference == move_preference &&
5063 move_dir == old_move_dir)
5065 /* prefer last direction when all directions are preferred equally */
5066 move_preference = move_dir_preference;
5067 new_move_dir = move_dir;
5071 MovDir[x][y] = new_move_dir;
5072 if (old_move_dir != new_move_dir)
5073 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5077 static void TurnRound(int x, int y)
5079 int direction = MovDir[x][y];
5083 GfxDir[x][y] = MovDir[x][y];
5085 if (direction != MovDir[x][y])
5089 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5092 static boolean JustBeingPushed(int x, int y)
5096 for (i = 0; i < MAX_PLAYERS; i++)
5098 struct PlayerInfo *player = &stored_player[i];
5100 if (player->active && player->is_pushing && player->MovPos)
5102 int next_jx = player->jx + (player->jx - player->last_jx);
5103 int next_jy = player->jy + (player->jy - player->last_jy);
5105 if (x == next_jx && y == next_jy)
5113 void StartMoving(int x, int y)
5115 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5116 int element = Feld[x][y];
5121 if (MovDelay[x][y] == 0)
5122 GfxAction[x][y] = ACTION_DEFAULT;
5124 if (CAN_FALL(element) && y < lev_fieldy - 1)
5126 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5127 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5128 if (JustBeingPushed(x, y))
5131 if (element == EL_QUICKSAND_FULL)
5133 if (IS_FREE(x, y + 1))
5135 InitMovingField(x, y, MV_DOWN);
5136 started_moving = TRUE;
5138 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5139 Store[x][y] = EL_ROCK;
5141 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5143 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5145 if (!MovDelay[x][y])
5146 MovDelay[x][y] = TILEY + 1;
5155 Feld[x][y] = EL_QUICKSAND_EMPTY;
5156 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5157 Store[x][y + 1] = Store[x][y];
5160 PlayLevelSoundAction(x, y, ACTION_FILLING);
5163 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5164 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5166 InitMovingField(x, y, MV_DOWN);
5167 started_moving = TRUE;
5169 Feld[x][y] = EL_QUICKSAND_FILLING;
5170 Store[x][y] = element;
5172 PlayLevelSoundAction(x, y, ACTION_FILLING);
5174 else if (element == EL_MAGIC_WALL_FULL)
5176 if (IS_FREE(x, y + 1))
5178 InitMovingField(x, y, MV_DOWN);
5179 started_moving = TRUE;
5181 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5182 Store[x][y] = EL_CHANGED(Store[x][y]);
5184 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5186 if (!MovDelay[x][y])
5187 MovDelay[x][y] = TILEY/4 + 1;
5196 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5197 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5198 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5202 else if (element == EL_BD_MAGIC_WALL_FULL)
5204 if (IS_FREE(x, y + 1))
5206 InitMovingField(x, y, MV_DOWN);
5207 started_moving = TRUE;
5209 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5210 Store[x][y] = EL_CHANGED2(Store[x][y]);
5212 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5214 if (!MovDelay[x][y])
5215 MovDelay[x][y] = TILEY/4 + 1;
5224 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5225 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5226 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5230 else if (CAN_PASS_MAGIC_WALL(element) &&
5231 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5232 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5234 InitMovingField(x, y, MV_DOWN);
5235 started_moving = TRUE;
5238 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5239 EL_BD_MAGIC_WALL_FILLING);
5240 Store[x][y] = element;
5242 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5244 SplashAcid(x, y + 1);
5246 InitMovingField(x, y, MV_DOWN);
5247 started_moving = TRUE;
5249 Store[x][y] = EL_ACID;
5251 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5252 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5254 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5255 CAN_FALL(element) && WasJustFalling[x][y] &&
5256 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5258 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5259 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5260 (Feld[x][y + 1] == EL_BLOCKED)))
5262 /* this is needed for a special case not covered by calling "Impact()"
5263 from "ContinueMoving()": if an element moves to a tile directly below
5264 another element which was just falling on that tile (which was empty
5265 in the previous frame), the falling element above would just stop
5266 instead of smashing the element below (in previous version, the above
5267 element was just checked for "moving" instead of "falling", resulting
5268 in incorrect smashes caused by horizontal movement of the above
5269 element; also, the case of the player being the element to smash was
5270 simply not covered here... :-/ ) */
5272 CheckCollision[x][y] = 0;
5276 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5278 if (MovDir[x][y] == MV_NONE)
5280 InitMovingField(x, y, MV_DOWN);
5281 started_moving = TRUE;
5284 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5286 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5287 MovDir[x][y] = MV_DOWN;
5289 InitMovingField(x, y, MV_DOWN);
5290 started_moving = TRUE;
5292 else if (element == EL_AMOEBA_DROP)
5294 Feld[x][y] = EL_AMOEBA_GROWING;
5295 Store[x][y] = EL_AMOEBA_WET;
5297 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5298 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5299 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5300 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5302 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5303 (IS_FREE(x - 1, y + 1) ||
5304 Feld[x - 1][y + 1] == EL_ACID));
5305 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5306 (IS_FREE(x + 1, y + 1) ||
5307 Feld[x + 1][y + 1] == EL_ACID));
5308 boolean can_fall_any = (can_fall_left || can_fall_right);
5309 boolean can_fall_both = (can_fall_left && can_fall_right);
5310 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5312 #if USE_NEW_ALL_SLIPPERY
5313 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5315 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5316 can_fall_right = FALSE;
5317 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5318 can_fall_left = FALSE;
5319 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5320 can_fall_right = FALSE;
5321 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5322 can_fall_left = FALSE;
5324 can_fall_any = (can_fall_left || can_fall_right);
5325 can_fall_both = FALSE;
5328 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5330 if (slippery_type == SLIPPERY_ONLY_LEFT)
5331 can_fall_right = FALSE;
5332 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5333 can_fall_left = FALSE;
5334 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5335 can_fall_right = FALSE;
5336 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5337 can_fall_left = FALSE;
5339 can_fall_any = (can_fall_left || can_fall_right);
5340 can_fall_both = (can_fall_left && can_fall_right);
5344 #if USE_NEW_ALL_SLIPPERY
5346 #if USE_NEW_SP_SLIPPERY
5347 /* !!! better use the same properties as for custom elements here !!! */
5348 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5349 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5351 can_fall_right = FALSE; /* slip down on left side */
5352 can_fall_both = FALSE;
5357 #if USE_NEW_ALL_SLIPPERY
5360 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5361 can_fall_right = FALSE; /* slip down on left side */
5363 can_fall_left = !(can_fall_right = RND(2));
5365 can_fall_both = FALSE;
5370 if (game.emulation == EMU_BOULDERDASH ||
5371 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5372 can_fall_right = FALSE; /* slip down on left side */
5374 can_fall_left = !(can_fall_right = RND(2));
5376 can_fall_both = FALSE;
5382 /* if not determined otherwise, prefer left side for slipping down */
5383 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5384 started_moving = TRUE;
5388 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5390 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5393 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5394 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5395 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5396 int belt_dir = game.belt_dir[belt_nr];
5398 if ((belt_dir == MV_LEFT && left_is_free) ||
5399 (belt_dir == MV_RIGHT && right_is_free))
5401 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5403 InitMovingField(x, y, belt_dir);
5404 started_moving = TRUE;
5406 Pushed[x][y] = TRUE;
5407 Pushed[nextx][y] = TRUE;
5409 GfxAction[x][y] = ACTION_DEFAULT;
5413 MovDir[x][y] = 0; /* if element was moving, stop it */
5418 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5420 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5422 if (CAN_MOVE(element) && !started_moving)
5425 int move_pattern = element_info[element].move_pattern;
5430 if (MovDir[x][y] == MV_NONE)
5432 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5433 x, y, element, element_info[element].token_name);
5434 printf("StartMoving(): This should never happen!\n");
5439 Moving2Blocked(x, y, &newx, &newy);
5441 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5444 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5445 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5447 WasJustMoving[x][y] = 0;
5448 CheckCollision[x][y] = 0;
5450 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5452 if (Feld[x][y] != element) /* element has changed */
5456 if (!MovDelay[x][y]) /* start new movement phase */
5458 /* all objects that can change their move direction after each step
5459 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5461 if (element != EL_YAMYAM &&
5462 element != EL_DARK_YAMYAM &&
5463 element != EL_PACMAN &&
5464 !(move_pattern & MV_ANY_DIRECTION) &&
5465 move_pattern != MV_TURNING_LEFT &&
5466 move_pattern != MV_TURNING_RIGHT &&
5467 move_pattern != MV_TURNING_LEFT_RIGHT &&
5468 move_pattern != MV_TURNING_RIGHT_LEFT &&
5469 move_pattern != MV_TURNING_RANDOM)
5473 if (MovDelay[x][y] && (element == EL_BUG ||
5474 element == EL_SPACESHIP ||
5475 element == EL_SP_SNIKSNAK ||
5476 element == EL_SP_ELECTRON ||
5477 element == EL_MOLE))
5478 DrawLevelField(x, y);
5482 if (MovDelay[x][y]) /* wait some time before next movement */
5486 if (element == EL_ROBOT ||
5487 element == EL_YAMYAM ||
5488 element == EL_DARK_YAMYAM)
5490 DrawLevelElementAnimationIfNeeded(x, y, element);
5491 PlayLevelSoundAction(x, y, ACTION_WAITING);
5493 else if (element == EL_SP_ELECTRON)
5494 DrawLevelElementAnimationIfNeeded(x, y, element);
5495 else if (element == EL_DRAGON)
5498 int dir = MovDir[x][y];
5499 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5500 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5501 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5502 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5503 dir == MV_UP ? IMG_FLAMES_1_UP :
5504 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5505 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5507 GfxAction[x][y] = ACTION_ATTACKING;
5509 if (IS_PLAYER(x, y))
5510 DrawPlayerField(x, y);
5512 DrawLevelField(x, y);
5514 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5516 for (i = 1; i <= 3; i++)
5518 int xx = x + i * dx;
5519 int yy = y + i * dy;
5520 int sx = SCREENX(xx);
5521 int sy = SCREENY(yy);
5522 int flame_graphic = graphic + (i - 1);
5524 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5529 int flamed = MovingOrBlocked2Element(xx, yy);
5533 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5535 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5536 RemoveMovingField(xx, yy);
5538 RemoveField(xx, yy);
5540 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5543 RemoveMovingField(xx, yy);
5546 ChangeDelay[xx][yy] = 0;
5548 Feld[xx][yy] = EL_FLAMES;
5550 if (IN_SCR_FIELD(sx, sy))
5552 DrawLevelFieldCrumbledSand(xx, yy);
5553 DrawGraphic(sx, sy, flame_graphic, frame);
5558 if (Feld[xx][yy] == EL_FLAMES)
5559 Feld[xx][yy] = EL_EMPTY;
5560 DrawLevelField(xx, yy);
5565 if (MovDelay[x][y]) /* element still has to wait some time */
5567 PlayLevelSoundAction(x, y, ACTION_WAITING);
5573 /* now make next step */
5575 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5577 if (DONT_COLLIDE_WITH(element) &&
5578 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5579 !PLAYER_ENEMY_PROTECTED(newx, newy))
5581 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5586 else if (CAN_MOVE_INTO_ACID(element) &&
5587 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5588 (MovDir[x][y] == MV_DOWN ||
5589 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5591 SplashAcid(newx, newy);
5592 Store[x][y] = EL_ACID;
5594 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5596 if (Feld[newx][newy] == EL_EXIT_OPEN)
5599 DrawLevelField(x, y);
5601 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5602 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5603 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5605 local_player->friends_still_needed--;
5606 if (!local_player->friends_still_needed &&
5607 !local_player->GameOver && AllPlayersGone)
5608 local_player->LevelSolved = local_player->GameOver = TRUE;
5612 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5614 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5615 DrawLevelField(newx, newy);
5617 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5619 else if (!IS_FREE(newx, newy))
5621 GfxAction[x][y] = ACTION_WAITING;
5623 if (IS_PLAYER(x, y))
5624 DrawPlayerField(x, y);
5626 DrawLevelField(x, y);
5631 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5633 if (IS_FOOD_PIG(Feld[newx][newy]))
5635 if (IS_MOVING(newx, newy))
5636 RemoveMovingField(newx, newy);
5639 Feld[newx][newy] = EL_EMPTY;
5640 DrawLevelField(newx, newy);
5643 PlayLevelSound(x, y, SND_PIG_DIGGING);
5645 else if (!IS_FREE(newx, newy))
5647 if (IS_PLAYER(x, y))
5648 DrawPlayerField(x, y);
5650 DrawLevelField(x, y);
5655 else if (IS_CUSTOM_ELEMENT(element) &&
5656 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5658 int new_element = Feld[newx][newy];
5660 if (!IS_FREE(newx, newy))
5662 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5663 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5666 /* no element can dig solid indestructible elements */
5667 if (IS_INDESTRUCTIBLE(new_element) &&
5668 !IS_DIGGABLE(new_element) &&
5669 !IS_COLLECTIBLE(new_element))
5672 if (AmoebaNr[newx][newy] &&
5673 (new_element == EL_AMOEBA_FULL ||
5674 new_element == EL_BD_AMOEBA ||
5675 new_element == EL_AMOEBA_GROWING))
5677 AmoebaCnt[AmoebaNr[newx][newy]]--;
5678 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5681 if (IS_MOVING(newx, newy))
5682 RemoveMovingField(newx, newy);
5685 RemoveField(newx, newy);
5686 DrawLevelField(newx, newy);
5689 /* if digged element was about to explode, prevent the explosion */
5690 ExplodeField[newx][newy] = EX_TYPE_NONE;
5692 PlayLevelSoundAction(x, y, action);
5695 Store[newx][newy] = EL_EMPTY;
5697 /* this makes it possible to leave the removed element again */
5698 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5699 Store[newx][newy] = new_element;
5701 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5703 int move_leave_element = element_info[element].move_leave_element;
5705 /* this makes it possible to leave the removed element again */
5706 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5707 new_element : move_leave_element);
5711 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5713 RunnerVisit[x][y] = FrameCounter;
5714 PlayerVisit[x][y] /= 8; /* expire player visit path */
5717 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5719 if (!IS_FREE(newx, newy))
5721 if (IS_PLAYER(x, y))
5722 DrawPlayerField(x, y);
5724 DrawLevelField(x, y);
5730 boolean wanna_flame = !RND(10);
5731 int dx = newx - x, dy = newy - y;
5732 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5733 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5734 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5735 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5736 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5737 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5740 IS_CLASSIC_ENEMY(element1) ||
5741 IS_CLASSIC_ENEMY(element2)) &&
5742 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5743 element1 != EL_FLAMES && element2 != EL_FLAMES)
5745 ResetGfxAnimation(x, y);
5746 GfxAction[x][y] = ACTION_ATTACKING;
5748 if (IS_PLAYER(x, y))
5749 DrawPlayerField(x, y);
5751 DrawLevelField(x, y);
5753 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5755 MovDelay[x][y] = 50;
5759 RemoveField(newx, newy);
5761 Feld[newx][newy] = EL_FLAMES;
5762 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5765 RemoveField(newx1, newy1);
5767 Feld[newx1][newy1] = EL_FLAMES;
5769 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5772 RemoveField(newx2, newy2);
5774 Feld[newx2][newy2] = EL_FLAMES;
5781 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5782 Feld[newx][newy] == EL_DIAMOND)
5784 if (IS_MOVING(newx, newy))
5785 RemoveMovingField(newx, newy);
5788 Feld[newx][newy] = EL_EMPTY;
5789 DrawLevelField(newx, newy);
5792 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5794 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5795 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5797 if (AmoebaNr[newx][newy])
5799 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5800 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5801 Feld[newx][newy] == EL_BD_AMOEBA)
5802 AmoebaCnt[AmoebaNr[newx][newy]]--;
5807 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5809 RemoveMovingField(newx, newy);
5812 if (IS_MOVING(newx, newy))
5814 RemoveMovingField(newx, newy);
5819 Feld[newx][newy] = EL_EMPTY;
5820 DrawLevelField(newx, newy);
5823 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5825 else if ((element == EL_PACMAN || element == EL_MOLE)
5826 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5828 if (AmoebaNr[newx][newy])
5830 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5831 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5832 Feld[newx][newy] == EL_BD_AMOEBA)
5833 AmoebaCnt[AmoebaNr[newx][newy]]--;
5836 if (element == EL_MOLE)
5838 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5839 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5841 ResetGfxAnimation(x, y);
5842 GfxAction[x][y] = ACTION_DIGGING;
5843 DrawLevelField(x, y);
5845 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5847 return; /* wait for shrinking amoeba */
5849 else /* element == EL_PACMAN */
5851 Feld[newx][newy] = EL_EMPTY;
5852 DrawLevelField(newx, newy);
5853 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5856 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5857 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5858 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5860 /* wait for shrinking amoeba to completely disappear */
5863 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5865 /* object was running against a wall */
5870 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5871 if (move_pattern & MV_ANY_DIRECTION &&
5872 move_pattern == MovDir[x][y])
5874 int blocking_element =
5875 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5877 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5880 element = Feld[x][y]; /* element might have changed */
5884 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5885 DrawLevelElementAnimation(x, y, element);
5887 if (DONT_TOUCH(element))
5888 TestIfBadThingTouchesPlayer(x, y);
5893 InitMovingField(x, y, MovDir[x][y]);
5895 PlayLevelSoundAction(x, y, ACTION_MOVING);
5899 ContinueMoving(x, y);
5902 void ContinueMoving(int x, int y)
5904 int element = Feld[x][y];
5905 struct ElementInfo *ei = &element_info[element];
5906 int direction = MovDir[x][y];
5907 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5908 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5909 int newx = x + dx, newy = y + dy;
5910 int stored = Store[x][y];
5911 int stored_new = Store[newx][newy];
5912 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5913 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5914 boolean last_line = (newy == lev_fieldy - 1);
5916 MovPos[x][y] += getElementMoveStepsize(x, y);
5918 if (pushed_by_player) /* special case: moving object pushed by player */
5919 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5921 if (ABS(MovPos[x][y]) < TILEX)
5923 DrawLevelField(x, y);
5925 return; /* element is still moving */
5928 /* element reached destination field */
5930 Feld[x][y] = EL_EMPTY;
5931 Feld[newx][newy] = element;
5932 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5934 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5936 element = Feld[newx][newy] = EL_ACID;
5938 else if (element == EL_MOLE)
5940 Feld[x][y] = EL_SAND;
5942 DrawLevelFieldCrumbledSandNeighbours(x, y);
5944 else if (element == EL_QUICKSAND_FILLING)
5946 element = Feld[newx][newy] = get_next_element(element);
5947 Store[newx][newy] = Store[x][y];
5949 else if (element == EL_QUICKSAND_EMPTYING)
5951 Feld[x][y] = get_next_element(element);
5952 element = Feld[newx][newy] = Store[x][y];
5954 else if (element == EL_MAGIC_WALL_FILLING)
5956 element = Feld[newx][newy] = get_next_element(element);
5957 if (!game.magic_wall_active)
5958 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5959 Store[newx][newy] = Store[x][y];
5961 else if (element == EL_MAGIC_WALL_EMPTYING)
5963 Feld[x][y] = get_next_element(element);
5964 if (!game.magic_wall_active)
5965 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5966 element = Feld[newx][newy] = Store[x][y];
5968 #if USE_NEW_CUSTOM_VALUE
5969 InitField(newx, newy, FALSE);
5972 else if (element == EL_BD_MAGIC_WALL_FILLING)
5974 element = Feld[newx][newy] = get_next_element(element);
5975 if (!game.magic_wall_active)
5976 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5977 Store[newx][newy] = Store[x][y];
5979 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5981 Feld[x][y] = get_next_element(element);
5982 if (!game.magic_wall_active)
5983 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5984 element = Feld[newx][newy] = Store[x][y];
5986 #if USE_NEW_CUSTOM_VALUE
5987 InitField(newx, newy, FALSE);
5990 else if (element == EL_AMOEBA_DROPPING)
5992 Feld[x][y] = get_next_element(element);
5993 element = Feld[newx][newy] = Store[x][y];
5995 else if (element == EL_SOKOBAN_OBJECT)
5998 Feld[x][y] = Back[x][y];
6000 if (Back[newx][newy])
6001 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6003 Back[x][y] = Back[newx][newy] = 0;
6006 Store[x][y] = EL_EMPTY;
6011 MovDelay[newx][newy] = 0;
6014 if (CAN_CHANGE_OR_HAS_ACTION(element))
6016 if (CAN_CHANGE(element))
6019 /* copy element change control values to new field */
6020 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6021 ChangePage[newx][newy] = ChangePage[x][y];
6022 ChangeCount[newx][newy] = ChangeCount[x][y];
6023 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6026 #if USE_NEW_CUSTOM_VALUE
6027 CustomValue[newx][newy] = CustomValue[x][y];
6033 #if USE_NEW_CUSTOM_VALUE
6034 CustomValue[newx][newy] = CustomValue[x][y];
6038 ChangeDelay[x][y] = 0;
6039 ChangePage[x][y] = -1;
6040 ChangeCount[x][y] = 0;
6041 ChangeEvent[x][y] = -1;
6043 #if USE_NEW_CUSTOM_VALUE
6044 CustomValue[x][y] = 0;
6047 /* copy animation control values to new field */
6048 GfxFrame[newx][newy] = GfxFrame[x][y];
6049 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6050 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6051 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6053 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6055 /* some elements can leave other elements behind after moving */
6056 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6057 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6058 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6060 int move_leave_element = ei->move_leave_element;
6063 /* this makes it possible to leave the removed element again */
6064 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6065 move_leave_element = stored;
6067 /* this makes it possible to leave the removed element again */
6068 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6069 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6070 move_leave_element = stored;
6073 Feld[x][y] = move_leave_element;
6075 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6076 MovDir[x][y] = direction;
6078 InitField(x, y, FALSE);
6080 if (GFX_CRUMBLED(Feld[x][y]))
6081 DrawLevelFieldCrumbledSandNeighbours(x, y);
6083 if (ELEM_IS_PLAYER(move_leave_element))
6084 RelocatePlayer(x, y, move_leave_element);
6087 /* do this after checking for left-behind element */
6088 ResetGfxAnimation(x, y); /* reset animation values for old field */
6090 if (!CAN_MOVE(element) ||
6091 (CAN_FALL(element) && direction == MV_DOWN &&
6092 (element == EL_SPRING ||
6093 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6094 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6095 GfxDir[x][y] = MovDir[newx][newy] = 0;
6097 DrawLevelField(x, y);
6098 DrawLevelField(newx, newy);
6100 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6102 /* prevent pushed element from moving on in pushed direction */
6103 if (pushed_by_player && CAN_MOVE(element) &&
6104 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6105 !(element_info[element].move_pattern & direction))
6106 TurnRound(newx, newy);
6108 /* prevent elements on conveyor belt from moving on in last direction */
6109 if (pushed_by_conveyor && CAN_FALL(element) &&
6110 direction & MV_HORIZONTAL)
6111 MovDir[newx][newy] = 0;
6113 if (!pushed_by_player)
6115 int nextx = newx + dx, nexty = newy + dy;
6116 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6118 WasJustMoving[newx][newy] = 3;
6120 if (CAN_FALL(element) && direction == MV_DOWN)
6121 WasJustFalling[newx][newy] = 3;
6123 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6124 CheckCollision[newx][newy] = 2;
6127 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6129 TestIfBadThingTouchesPlayer(newx, newy);
6130 TestIfBadThingTouchesFriend(newx, newy);
6132 if (!IS_CUSTOM_ELEMENT(element))
6133 TestIfBadThingTouchesOtherBadThing(newx, newy);
6135 else if (element == EL_PENGUIN)
6136 TestIfFriendTouchesBadThing(newx, newy);
6138 /* give the player one last chance (one more frame) to move away */
6139 if (CAN_FALL(element) && direction == MV_DOWN &&
6140 (last_line || (!IS_FREE(x, newy + 1) &&
6141 (!IS_PLAYER(x, newy + 1) ||
6142 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6145 if (pushed_by_player && !game.use_change_when_pushing_bug)
6147 int push_side = MV_DIR_OPPOSITE(direction);
6148 struct PlayerInfo *player = PLAYERINFO(x, y);
6150 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6151 player->index_bit, push_side);
6152 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6153 player->index_bit, push_side);
6156 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6158 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6160 TestIfElementHitsCustomElement(newx, newy, direction);
6161 TestIfPlayerTouchesCustomElement(newx, newy);
6162 TestIfElementTouchesCustomElement(newx, newy);
6165 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6166 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6167 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6168 MV_DIR_OPPOSITE(direction));
6172 int AmoebeNachbarNr(int ax, int ay)
6175 int element = Feld[ax][ay];
6177 static int xy[4][2] =
6185 for (i = 0; i < NUM_DIRECTIONS; i++)
6187 int x = ax + xy[i][0];
6188 int y = ay + xy[i][1];
6190 if (!IN_LEV_FIELD(x, y))
6193 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6194 group_nr = AmoebaNr[x][y];
6200 void AmoebenVereinigen(int ax, int ay)
6202 int i, x, y, xx, yy;
6203 int new_group_nr = AmoebaNr[ax][ay];
6204 static int xy[4][2] =
6212 if (new_group_nr == 0)
6215 for (i = 0; i < NUM_DIRECTIONS; i++)
6220 if (!IN_LEV_FIELD(x, y))
6223 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6224 Feld[x][y] == EL_BD_AMOEBA ||
6225 Feld[x][y] == EL_AMOEBA_DEAD) &&
6226 AmoebaNr[x][y] != new_group_nr)
6228 int old_group_nr = AmoebaNr[x][y];
6230 if (old_group_nr == 0)
6233 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6234 AmoebaCnt[old_group_nr] = 0;
6235 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6236 AmoebaCnt2[old_group_nr] = 0;
6239 SCAN_PLAYFIELD(xx, yy)
6241 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6244 if (AmoebaNr[xx][yy] == old_group_nr)
6245 AmoebaNr[xx][yy] = new_group_nr;
6251 void AmoebeUmwandeln(int ax, int ay)
6255 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6257 int group_nr = AmoebaNr[ax][ay];
6262 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6263 printf("AmoebeUmwandeln(): This should never happen!\n");
6269 SCAN_PLAYFIELD(x, y)
6271 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6274 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6277 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6281 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6282 SND_AMOEBA_TURNING_TO_GEM :
6283 SND_AMOEBA_TURNING_TO_ROCK));
6288 static int xy[4][2] =
6296 for (i = 0; i < NUM_DIRECTIONS; i++)
6301 if (!IN_LEV_FIELD(x, y))
6304 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6306 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6307 SND_AMOEBA_TURNING_TO_GEM :
6308 SND_AMOEBA_TURNING_TO_ROCK));
6315 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6318 int group_nr = AmoebaNr[ax][ay];
6319 boolean done = FALSE;
6324 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6325 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6331 SCAN_PLAYFIELD(x, y)
6333 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6336 if (AmoebaNr[x][y] == group_nr &&
6337 (Feld[x][y] == EL_AMOEBA_DEAD ||
6338 Feld[x][y] == EL_BD_AMOEBA ||
6339 Feld[x][y] == EL_AMOEBA_GROWING))
6342 Feld[x][y] = new_element;
6343 InitField(x, y, FALSE);
6344 DrawLevelField(x, y);
6350 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6351 SND_BD_AMOEBA_TURNING_TO_ROCK :
6352 SND_BD_AMOEBA_TURNING_TO_GEM));
6355 void AmoebeWaechst(int x, int y)
6357 static unsigned long sound_delay = 0;
6358 static unsigned long sound_delay_value = 0;
6360 if (!MovDelay[x][y]) /* start new growing cycle */
6364 if (DelayReached(&sound_delay, sound_delay_value))
6366 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6367 sound_delay_value = 30;
6371 if (MovDelay[x][y]) /* wait some time before growing bigger */
6374 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6376 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6377 6 - MovDelay[x][y]);
6379 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6382 if (!MovDelay[x][y])
6384 Feld[x][y] = Store[x][y];
6386 DrawLevelField(x, y);
6391 void AmoebaDisappearing(int x, int y)
6393 static unsigned long sound_delay = 0;
6394 static unsigned long sound_delay_value = 0;
6396 if (!MovDelay[x][y]) /* start new shrinking cycle */
6400 if (DelayReached(&sound_delay, sound_delay_value))
6401 sound_delay_value = 30;
6404 if (MovDelay[x][y]) /* wait some time before shrinking */
6407 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6409 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6410 6 - MovDelay[x][y]);
6412 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6415 if (!MovDelay[x][y])
6417 Feld[x][y] = EL_EMPTY;
6418 DrawLevelField(x, y);
6420 /* don't let mole enter this field in this cycle;
6421 (give priority to objects falling to this field from above) */
6427 void AmoebeAbleger(int ax, int ay)
6430 int element = Feld[ax][ay];
6431 int graphic = el2img(element);
6432 int newax = ax, neway = ay;
6433 static int xy[4][2] =
6441 if (!level.amoeba_speed)
6443 Feld[ax][ay] = EL_AMOEBA_DEAD;
6444 DrawLevelField(ax, ay);
6448 if (IS_ANIMATED(graphic))
6449 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6451 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6452 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6454 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6457 if (MovDelay[ax][ay])
6461 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6464 int x = ax + xy[start][0];
6465 int y = ay + xy[start][1];
6467 if (!IN_LEV_FIELD(x, y))
6470 if (IS_FREE(x, y) ||
6471 CAN_GROW_INTO(Feld[x][y]) ||
6472 Feld[x][y] == EL_QUICKSAND_EMPTY)
6478 if (newax == ax && neway == ay)
6481 else /* normal or "filled" (BD style) amoeba */
6484 boolean waiting_for_player = FALSE;
6486 for (i = 0; i < NUM_DIRECTIONS; i++)
6488 int j = (start + i) % 4;
6489 int x = ax + xy[j][0];
6490 int y = ay + xy[j][1];
6492 if (!IN_LEV_FIELD(x, y))
6495 if (IS_FREE(x, y) ||
6496 CAN_GROW_INTO(Feld[x][y]) ||
6497 Feld[x][y] == EL_QUICKSAND_EMPTY)
6503 else if (IS_PLAYER(x, y))
6504 waiting_for_player = TRUE;
6507 if (newax == ax && neway == ay) /* amoeba cannot grow */
6509 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6511 Feld[ax][ay] = EL_AMOEBA_DEAD;
6512 DrawLevelField(ax, ay);
6513 AmoebaCnt[AmoebaNr[ax][ay]]--;
6515 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6517 if (element == EL_AMOEBA_FULL)
6518 AmoebeUmwandeln(ax, ay);
6519 else if (element == EL_BD_AMOEBA)
6520 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6525 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6527 /* amoeba gets larger by growing in some direction */
6529 int new_group_nr = AmoebaNr[ax][ay];
6532 if (new_group_nr == 0)
6534 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6535 printf("AmoebeAbleger(): This should never happen!\n");
6540 AmoebaNr[newax][neway] = new_group_nr;
6541 AmoebaCnt[new_group_nr]++;
6542 AmoebaCnt2[new_group_nr]++;
6544 /* if amoeba touches other amoeba(s) after growing, unify them */
6545 AmoebenVereinigen(newax, neway);
6547 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6549 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6555 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6556 (neway == lev_fieldy - 1 && newax != ax))
6558 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6559 Store[newax][neway] = element;
6561 else if (neway == ay)
6563 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6565 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6569 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6570 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6571 Store[ax][ay] = EL_AMOEBA_DROP;
6572 ContinueMoving(ax, ay);
6576 DrawLevelField(newax, neway);
6579 void Life(int ax, int ay)
6583 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6586 int element = Feld[ax][ay];
6587 int graphic = el2img(element);
6588 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6590 boolean changed = FALSE;
6592 if (IS_ANIMATED(graphic))
6593 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6598 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6599 MovDelay[ax][ay] = life_time;
6601 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6604 if (MovDelay[ax][ay])
6608 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6610 int xx = ax+x1, yy = ay+y1;
6613 if (!IN_LEV_FIELD(xx, yy))
6616 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6618 int x = xx+x2, y = yy+y2;
6620 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6623 if (((Feld[x][y] == element ||
6624 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6626 (IS_FREE(x, y) && Stop[x][y]))
6630 if (xx == ax && yy == ay) /* field in the middle */
6632 if (nachbarn < life_parameter[0] ||
6633 nachbarn > life_parameter[1])
6635 Feld[xx][yy] = EL_EMPTY;
6637 DrawLevelField(xx, yy);
6638 Stop[xx][yy] = TRUE;
6642 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6643 { /* free border field */
6644 if (nachbarn >= life_parameter[2] &&
6645 nachbarn <= life_parameter[3])
6647 Feld[xx][yy] = element;
6648 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6650 DrawLevelField(xx, yy);
6651 Stop[xx][yy] = TRUE;
6658 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6659 SND_GAME_OF_LIFE_GROWING);
6662 static void InitRobotWheel(int x, int y)
6664 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6667 static void RunRobotWheel(int x, int y)
6669 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6672 static void StopRobotWheel(int x, int y)
6674 if (ZX == x && ZY == y)
6678 static void InitTimegateWheel(int x, int y)
6680 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6683 static void RunTimegateWheel(int x, int y)
6685 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6688 static void InitMagicBallDelay(int x, int y)
6691 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
6693 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
6697 static void ActivateMagicBall(int bx, int by)
6701 if (level.ball_random)
6703 int pos_border = RND(8); /* select one of the eight border elements */
6704 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
6705 int xx = pos_content % 3;
6706 int yy = pos_content / 3;
6711 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6712 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6716 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
6718 int xx = x - bx + 1;
6719 int yy = y - by + 1;
6721 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6722 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6726 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
6729 void CheckExit(int x, int y)
6731 if (local_player->gems_still_needed > 0 ||
6732 local_player->sokobanfields_still_needed > 0 ||
6733 local_player->lights_still_needed > 0)
6735 int element = Feld[x][y];
6736 int graphic = el2img(element);
6738 if (IS_ANIMATED(graphic))
6739 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6744 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6747 Feld[x][y] = EL_EXIT_OPENING;
6749 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6752 void CheckExitSP(int x, int y)
6754 if (local_player->gems_still_needed > 0)
6756 int element = Feld[x][y];
6757 int graphic = el2img(element);
6759 if (IS_ANIMATED(graphic))
6760 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6765 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6768 Feld[x][y] = EL_SP_EXIT_OPENING;
6770 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6773 static void CloseAllOpenTimegates()
6778 SCAN_PLAYFIELD(x, y)
6780 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6783 int element = Feld[x][y];
6785 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6787 Feld[x][y] = EL_TIMEGATE_CLOSING;
6789 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6794 void EdelsteinFunkeln(int x, int y)
6796 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6799 if (Feld[x][y] == EL_BD_DIAMOND)
6802 if (MovDelay[x][y] == 0) /* next animation frame */
6803 MovDelay[x][y] = 11 * !SimpleRND(500);
6805 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6809 if (setup.direct_draw && MovDelay[x][y])
6810 SetDrawtoField(DRAW_BUFFERED);
6812 DrawLevelElementAnimation(x, y, Feld[x][y]);
6814 if (MovDelay[x][y] != 0)
6816 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6817 10 - MovDelay[x][y]);
6819 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6821 if (setup.direct_draw)
6825 dest_x = FX + SCREENX(x) * TILEX;
6826 dest_y = FY + SCREENY(y) * TILEY;
6828 BlitBitmap(drawto_field, window,
6829 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6830 SetDrawtoField(DRAW_DIRECT);
6836 void MauerWaechst(int x, int y)
6840 if (!MovDelay[x][y]) /* next animation frame */
6841 MovDelay[x][y] = 3 * delay;
6843 if (MovDelay[x][y]) /* wait some time before next frame */
6847 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6849 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6850 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6852 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6855 if (!MovDelay[x][y])
6857 if (MovDir[x][y] == MV_LEFT)
6859 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6860 DrawLevelField(x - 1, y);
6862 else if (MovDir[x][y] == MV_RIGHT)
6864 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6865 DrawLevelField(x + 1, y);
6867 else if (MovDir[x][y] == MV_UP)
6869 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6870 DrawLevelField(x, y - 1);
6874 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6875 DrawLevelField(x, y + 1);
6878 Feld[x][y] = Store[x][y];
6880 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6881 DrawLevelField(x, y);
6886 void MauerAbleger(int ax, int ay)
6888 int element = Feld[ax][ay];
6889 int graphic = el2img(element);
6890 boolean oben_frei = FALSE, unten_frei = FALSE;
6891 boolean links_frei = FALSE, rechts_frei = FALSE;
6892 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6893 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6894 boolean new_wall = FALSE;
6896 if (IS_ANIMATED(graphic))
6897 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6899 if (!MovDelay[ax][ay]) /* start building new wall */
6900 MovDelay[ax][ay] = 6;
6902 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6905 if (MovDelay[ax][ay])
6909 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6911 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6913 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6915 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6918 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6919 element == EL_EXPANDABLE_WALL_ANY)
6923 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6924 Store[ax][ay-1] = element;
6925 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6926 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6927 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6928 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6933 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6934 Store[ax][ay+1] = element;
6935 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6936 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6937 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6938 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6943 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6944 element == EL_EXPANDABLE_WALL_ANY ||
6945 element == EL_EXPANDABLE_WALL)
6949 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6950 Store[ax-1][ay] = element;
6951 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6952 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6953 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6954 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6960 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6961 Store[ax+1][ay] = element;
6962 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6963 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6964 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6965 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6970 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6971 DrawLevelField(ax, ay);
6973 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6975 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6976 unten_massiv = TRUE;
6977 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6978 links_massiv = TRUE;
6979 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6980 rechts_massiv = TRUE;
6982 if (((oben_massiv && unten_massiv) ||
6983 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6984 element == EL_EXPANDABLE_WALL) &&
6985 ((links_massiv && rechts_massiv) ||
6986 element == EL_EXPANDABLE_WALL_VERTICAL))
6987 Feld[ax][ay] = EL_WALL;
6990 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6993 void CheckForDragon(int x, int y)
6996 boolean dragon_found = FALSE;
6997 static int xy[4][2] =
7005 for (i = 0; i < NUM_DIRECTIONS; i++)
7007 for (j = 0; j < 4; j++)
7009 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7011 if (IN_LEV_FIELD(xx, yy) &&
7012 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7014 if (Feld[xx][yy] == EL_DRAGON)
7015 dragon_found = TRUE;
7024 for (i = 0; i < NUM_DIRECTIONS; i++)
7026 for (j = 0; j < 3; j++)
7028 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7030 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7032 Feld[xx][yy] = EL_EMPTY;
7033 DrawLevelField(xx, yy);
7042 static void InitBuggyBase(int x, int y)
7044 int element = Feld[x][y];
7045 int activating_delay = FRAMES_PER_SECOND / 4;
7048 (element == EL_SP_BUGGY_BASE ?
7049 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7050 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7052 element == EL_SP_BUGGY_BASE_ACTIVE ?
7053 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7056 static void WarnBuggyBase(int x, int y)
7059 static int xy[4][2] =
7067 for (i = 0; i < NUM_DIRECTIONS; i++)
7069 int xx = x + xy[i][0], yy = y + xy[i][1];
7071 if (IS_PLAYER(xx, yy))
7073 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7080 static void InitTrap(int x, int y)
7082 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7085 static void ActivateTrap(int x, int y)
7087 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7090 static void ChangeActiveTrap(int x, int y)
7092 int graphic = IMG_TRAP_ACTIVE;
7094 /* if new animation frame was drawn, correct crumbled sand border */
7095 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7096 DrawLevelFieldCrumbledSand(x, y);
7099 static int getSpecialActionElement(int element, int number, int base_element)
7101 return (element != EL_EMPTY ? element :
7102 number != -1 ? base_element + number - 1 :
7106 static int getModifiedActionNumber(int value_old, int operator, int operand,
7107 int value_min, int value_max)
7109 int value_new = (operator == CA_MODE_SET ? operand :
7110 operator == CA_MODE_ADD ? value_old + operand :
7111 operator == CA_MODE_SUBTRACT ? value_old - operand :
7112 operator == CA_MODE_MULTIPLY ? value_old * operand :
7113 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7114 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7117 return (value_new < value_min ? value_min :
7118 value_new > value_max ? value_max :
7122 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7124 struct ElementInfo *ei = &element_info[element];
7125 struct ElementChangeInfo *change = &ei->change_page[page];
7126 int action_type = change->action_type;
7127 int action_mode = change->action_mode;
7128 int action_arg = change->action_arg;
7131 if (!change->has_action)
7134 /* ---------- determine action paramater values -------------------------- */
7136 int level_time_value =
7137 (level.time > 0 ? TimeLeft :
7140 int action_arg_element =
7141 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7142 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7143 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7146 int action_arg_direction =
7147 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7148 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7149 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7150 change->actual_trigger_side :
7151 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7152 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7155 int action_arg_number_min =
7156 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7159 int action_arg_number_max =
7160 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7161 action_type == CA_SET_LEVEL_GEMS ? 999 :
7162 action_type == CA_SET_LEVEL_TIME ? 9999 :
7163 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7164 action_type == CA_SET_CE_SCORE ? 9999 :
7165 action_type == CA_SET_CE_VALUE ? 9999 :
7168 int action_arg_number_reset =
7169 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7170 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7171 action_type == CA_SET_LEVEL_TIME ? level.time :
7172 action_type == CA_SET_LEVEL_SCORE ? 0 :
7173 action_type == CA_SET_CE_SCORE ? 0 :
7175 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7177 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7181 int action_arg_number =
7182 (action_arg <= CA_ARG_MAX ? action_arg :
7183 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7184 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7185 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7186 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7187 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7188 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7189 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7190 #if USE_NEW_CUSTOM_VALUE
7191 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7193 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7195 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7196 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7197 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7198 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7199 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7200 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
7203 int action_arg_number_old =
7204 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7205 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7206 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7207 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7208 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7211 int action_arg_number_new =
7212 getModifiedActionNumber(action_arg_number_old,
7213 action_mode, action_arg_number,
7214 action_arg_number_min, action_arg_number_max);
7216 int trigger_player_bits =
7217 (change->actual_trigger_player >= EL_PLAYER_1 &&
7218 change->actual_trigger_player <= EL_PLAYER_4 ?
7219 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7222 int action_arg_player_bits =
7223 (action_arg >= CA_ARG_PLAYER_1 &&
7224 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7225 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7228 /* ---------- execute action -------------------------------------------- */
7237 /* ---------- level actions ------------------------------------------- */
7239 case CA_RESTART_LEVEL:
7241 game.restart_level = TRUE;
7246 case CA_SHOW_ENVELOPE:
7248 int element = getSpecialActionElement(action_arg_element,
7249 action_arg_number, EL_ENVELOPE_1);
7251 if (IS_ENVELOPE(element))
7252 local_player->show_envelope = element;
7257 case CA_SET_LEVEL_TIME:
7259 if (level.time > 0) /* only modify limited time value */
7261 TimeLeft = action_arg_number_new;
7263 DrawGameValue_Time(TimeLeft);
7265 if (!TimeLeft && setup.time_limit)
7266 for (i = 0; i < MAX_PLAYERS; i++)
7267 KillPlayer(&stored_player[i]);
7273 case CA_SET_LEVEL_SCORE:
7275 local_player->score = action_arg_number_new;
7277 DrawGameValue_Score(local_player->score);
7282 case CA_SET_LEVEL_GEMS:
7284 local_player->gems_still_needed = action_arg_number_new;
7286 DrawGameValue_Emeralds(local_player->gems_still_needed);
7291 case CA_SET_LEVEL_GRAVITY:
7293 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7294 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7295 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7300 case CA_SET_LEVEL_WIND:
7302 game.wind_direction = action_arg_direction;
7307 /* ---------- player actions ------------------------------------------ */
7309 case CA_MOVE_PLAYER:
7311 /* automatically move to the next field in specified direction */
7312 for (i = 0; i < MAX_PLAYERS; i++)
7313 if (trigger_player_bits & (1 << i))
7314 stored_player[i].programmed_action = action_arg_direction;
7319 case CA_EXIT_PLAYER:
7321 for (i = 0; i < MAX_PLAYERS; i++)
7322 if (action_arg_player_bits & (1 << i))
7323 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7328 case CA_KILL_PLAYER:
7330 for (i = 0; i < MAX_PLAYERS; i++)
7331 if (action_arg_player_bits & (1 << i))
7332 KillPlayer(&stored_player[i]);
7337 case CA_SET_PLAYER_KEYS:
7339 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7340 int element = getSpecialActionElement(action_arg_element,
7341 action_arg_number, EL_KEY_1);
7343 if (IS_KEY(element))
7345 for (i = 0; i < MAX_PLAYERS; i++)
7347 if (trigger_player_bits & (1 << i))
7349 stored_player[i].key[KEY_NR(element)] = key_state;
7351 DrawGameValue_Keys(stored_player[i].key);
7353 redraw_mask |= REDRAW_DOOR_1;
7361 case CA_SET_PLAYER_SPEED:
7363 for (i = 0; i < MAX_PLAYERS; i++)
7365 if (trigger_player_bits & (1 << i))
7367 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7369 if (action_arg == CA_ARG_SPEED_FASTER &&
7370 stored_player[i].cannot_move)
7372 action_arg_number = STEPSIZE_VERY_SLOW;
7374 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7375 action_arg == CA_ARG_SPEED_FASTER)
7377 action_arg_number = 2;
7378 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7383 getModifiedActionNumber(move_stepsize,
7386 action_arg_number_min,
7387 action_arg_number_max);
7390 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7392 /* make sure that value is power of 2 */
7393 move_stepsize = (1 << log_2(move_stepsize));
7395 /* do no immediately change -- the player might just be moving */
7396 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7398 stored_player[i].cannot_move =
7399 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7407 case CA_SET_PLAYER_SHIELD:
7409 for (i = 0; i < MAX_PLAYERS; i++)
7411 if (trigger_player_bits & (1 << i))
7413 if (action_arg == CA_ARG_SHIELD_OFF)
7415 stored_player[i].shield_normal_time_left = 0;
7416 stored_player[i].shield_deadly_time_left = 0;
7418 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7420 stored_player[i].shield_normal_time_left = 999999;
7422 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7424 stored_player[i].shield_normal_time_left = 999999;
7425 stored_player[i].shield_deadly_time_left = 999999;
7433 case CA_SET_PLAYER_ARTWORK:
7435 for (i = 0; i < MAX_PLAYERS; i++)
7437 if (trigger_player_bits & (1 << i))
7439 int artwork_element = action_arg_element;
7441 if (action_arg == CA_ARG_ELEMENT_RESET)
7443 (level.use_artwork_element[i] ? level.artwork_element[i] :
7444 stored_player[i].element_nr);
7446 stored_player[i].artwork_element = artwork_element;
7448 SetPlayerWaiting(&stored_player[i], FALSE);
7450 /* set number of special actions for bored and sleeping animation */
7451 stored_player[i].num_special_action_bored =
7452 get_num_special_action(artwork_element,
7453 ACTION_BORING_1, ACTION_BORING_LAST);
7454 stored_player[i].num_special_action_sleeping =
7455 get_num_special_action(artwork_element,
7456 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7463 /* ---------- CE actions ---------------------------------------------- */
7465 case CA_SET_CE_SCORE:
7467 ei->collect_score = action_arg_number_new;
7472 case CA_SET_CE_VALUE:
7474 #if USE_NEW_CUSTOM_VALUE
7475 int last_custom_value = CustomValue[x][y];
7477 CustomValue[x][y] = action_arg_number_new;
7480 printf("::: Count == %d\n", CustomValue[x][y]);
7483 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7486 printf("::: CE_VALUE_GETS_ZERO\n");
7489 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7490 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7497 /* ---------- engine actions ------------------------------------------ */
7499 case CA_SET_ENGINE_SCAN_MODE:
7501 InitPlayfieldScanMode(action_arg);
7511 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7513 int previous_move_direction = MovDir[x][y];
7514 #if USE_NEW_CUSTOM_VALUE
7515 int last_ce_value = CustomValue[x][y];
7517 boolean add_player = (ELEM_IS_PLAYER(element) &&
7518 IS_WALKABLE(Feld[x][y]));
7520 /* check if element under player changes from accessible to unaccessible
7521 (needed for special case of dropping element which then changes) */
7522 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7523 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7532 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7533 RemoveMovingField(x, y);
7537 Feld[x][y] = element;
7539 ResetGfxAnimation(x, y);
7540 ResetRandomAnimationValue(x, y);
7542 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7543 MovDir[x][y] = previous_move_direction;
7545 #if USE_NEW_CUSTOM_VALUE
7546 if (element_info[Feld[x][y]].use_last_ce_value)
7547 CustomValue[x][y] = last_ce_value;
7550 InitField_WithBug1(x, y, FALSE);
7552 DrawLevelField(x, y);
7554 if (GFX_CRUMBLED(Feld[x][y]))
7555 DrawLevelFieldCrumbledSandNeighbours(x, y);
7558 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7559 if (ELEM_IS_PLAYER(element))
7560 RelocatePlayer(x, y, element);
7563 ChangeCount[x][y]++; /* count number of changes in the same frame */
7565 TestIfBadThingTouchesPlayer(x, y);
7566 TestIfPlayerTouchesCustomElement(x, y);
7567 TestIfElementTouchesCustomElement(x, y);
7570 static void CreateField(int x, int y, int element)
7572 CreateFieldExt(x, y, element, FALSE);
7575 static void CreateElementFromChange(int x, int y, int element)
7577 CreateFieldExt(x, y, element, TRUE);
7580 static boolean ChangeElement(int x, int y, int element, int page)
7582 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7584 int old_element = Feld[x][y];
7586 /* always use default change event to prevent running into a loop */
7587 if (ChangeEvent[x][y] == -1)
7588 ChangeEvent[x][y] = CE_DELAY;
7590 if (ChangeEvent[x][y] == CE_DELAY)
7592 /* reset actual trigger element, trigger player and action element */
7593 change->actual_trigger_element = EL_EMPTY;
7594 change->actual_trigger_player = EL_PLAYER_1;
7595 change->actual_trigger_side = CH_SIDE_NONE;
7596 change->actual_trigger_ce_value = 0;
7599 /* do not change elements more than a specified maximum number of changes */
7600 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7603 ChangeCount[x][y]++; /* count number of changes in the same frame */
7605 if (change->explode)
7612 if (change->use_target_content)
7614 boolean complete_replace = TRUE;
7615 boolean can_replace[3][3];
7618 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7621 boolean is_walkable;
7622 boolean is_diggable;
7623 boolean is_collectible;
7624 boolean is_removable;
7625 boolean is_destructible;
7626 int ex = x + xx - 1;
7627 int ey = y + yy - 1;
7628 int content_element = change->target_content.e[xx][yy];
7631 can_replace[xx][yy] = TRUE;
7633 if (ex == x && ey == y) /* do not check changing element itself */
7636 if (content_element == EL_EMPTY_SPACE)
7638 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7643 if (!IN_LEV_FIELD(ex, ey))
7645 can_replace[xx][yy] = FALSE;
7646 complete_replace = FALSE;
7653 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7654 e = MovingOrBlocked2Element(ex, ey);
7656 is_empty = (IS_FREE(ex, ey) ||
7657 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7659 is_walkable = (is_empty || IS_WALKABLE(e));
7660 is_diggable = (is_empty || IS_DIGGABLE(e));
7661 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7662 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7663 is_removable = (is_diggable || is_collectible);
7665 can_replace[xx][yy] =
7666 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7667 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7668 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7669 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7670 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7671 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7672 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7674 if (!can_replace[xx][yy])
7675 complete_replace = FALSE;
7678 if (!change->only_if_complete || complete_replace)
7680 boolean something_has_changed = FALSE;
7682 if (change->only_if_complete && change->use_random_replace &&
7683 RND(100) < change->random_percentage)
7686 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7688 int ex = x + xx - 1;
7689 int ey = y + yy - 1;
7690 int content_element;
7692 if (can_replace[xx][yy] && (!change->use_random_replace ||
7693 RND(100) < change->random_percentage))
7695 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7696 RemoveMovingField(ex, ey);
7698 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7700 content_element = change->target_content.e[xx][yy];
7701 target_element = GET_TARGET_ELEMENT(content_element, change);
7703 CreateElementFromChange(ex, ey, target_element);
7705 something_has_changed = TRUE;
7707 /* for symmetry reasons, freeze newly created border elements */
7708 if (ex != x || ey != y)
7709 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7713 if (something_has_changed)
7715 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7716 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7722 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7724 CreateElementFromChange(x, y, target_element);
7726 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7727 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7730 /* this uses direct change before indirect change */
7731 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7736 #if USE_NEW_DELAYED_ACTION
7738 static void HandleElementChange(int x, int y, int page)
7740 int element = MovingOrBlocked2Element(x, y);
7741 struct ElementInfo *ei = &element_info[element];
7742 struct ElementChangeInfo *change = &ei->change_page[page];
7745 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7746 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7749 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7750 x, y, element, element_info[element].token_name);
7751 printf("HandleElementChange(): This should never happen!\n");
7756 /* this can happen with classic bombs on walkable, changing elements */
7757 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7760 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7761 ChangeDelay[x][y] = 0;
7767 if (ChangeDelay[x][y] == 0) /* initialize element change */
7769 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7771 if (change->can_change)
7773 ResetGfxAnimation(x, y);
7774 ResetRandomAnimationValue(x, y);
7776 if (change->pre_change_function)
7777 change->pre_change_function(x, y);
7781 ChangeDelay[x][y]--;
7783 if (ChangeDelay[x][y] != 0) /* continue element change */
7785 if (change->can_change)
7787 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7789 if (IS_ANIMATED(graphic))
7790 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7792 if (change->change_function)
7793 change->change_function(x, y);
7796 else /* finish element change */
7798 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7800 page = ChangePage[x][y];
7801 ChangePage[x][y] = -1;
7803 change = &ei->change_page[page];
7806 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7808 ChangeDelay[x][y] = 1; /* try change after next move step */
7809 ChangePage[x][y] = page; /* remember page to use for change */
7814 if (change->can_change)
7816 if (ChangeElement(x, y, element, page))
7818 if (change->post_change_function)
7819 change->post_change_function(x, y);
7823 if (change->has_action)
7824 ExecuteCustomElementAction(x, y, element, page);
7830 static void HandleElementChange(int x, int y, int page)
7832 int element = MovingOrBlocked2Element(x, y);
7833 struct ElementInfo *ei = &element_info[element];
7834 struct ElementChangeInfo *change = &ei->change_page[page];
7837 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7840 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7841 x, y, element, element_info[element].token_name);
7842 printf("HandleElementChange(): This should never happen!\n");
7847 /* this can happen with classic bombs on walkable, changing elements */
7848 if (!CAN_CHANGE(element))
7851 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7852 ChangeDelay[x][y] = 0;
7858 if (ChangeDelay[x][y] == 0) /* initialize element change */
7860 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7862 ResetGfxAnimation(x, y);
7863 ResetRandomAnimationValue(x, y);
7865 if (change->pre_change_function)
7866 change->pre_change_function(x, y);
7869 ChangeDelay[x][y]--;
7871 if (ChangeDelay[x][y] != 0) /* continue element change */
7873 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7875 if (IS_ANIMATED(graphic))
7876 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7878 if (change->change_function)
7879 change->change_function(x, y);
7881 else /* finish element change */
7883 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7885 page = ChangePage[x][y];
7886 ChangePage[x][y] = -1;
7888 change = &ei->change_page[page];
7891 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7893 ChangeDelay[x][y] = 1; /* try change after next move step */
7894 ChangePage[x][y] = page; /* remember page to use for change */
7899 if (ChangeElement(x, y, element, page))
7901 if (change->post_change_function)
7902 change->post_change_function(x, y);
7909 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
7910 int trigger_element,
7916 boolean change_done_any = FALSE;
7917 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7920 if (!(trigger_events[trigger_element][trigger_event]))
7923 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7925 int element = EL_CUSTOM_START + i;
7926 boolean change_done = FALSE;
7929 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7930 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7933 for (p = 0; p < element_info[element].num_change_pages; p++)
7935 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7937 if (change->can_change_or_has_action &&
7938 change->has_event[trigger_event] &&
7939 change->trigger_side & trigger_side &&
7940 change->trigger_player & trigger_player &&
7941 change->trigger_page & trigger_page_bits &&
7942 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7944 change->actual_trigger_element = trigger_element;
7945 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7946 change->actual_trigger_side = trigger_side;
7947 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
7949 if ((change->can_change && !change_done) || change->has_action)
7954 SCAN_PLAYFIELD(x, y)
7956 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7959 if (Feld[x][y] == element)
7961 if (change->can_change && !change_done)
7963 ChangeDelay[x][y] = 1;
7964 ChangeEvent[x][y] = trigger_event;
7966 HandleElementChange(x, y, p);
7968 #if USE_NEW_DELAYED_ACTION
7969 else if (change->has_action)
7971 ExecuteCustomElementAction(x, y, element, p);
7972 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7975 if (change->has_action)
7977 ExecuteCustomElementAction(x, y, element, p);
7978 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7984 if (change->can_change)
7987 change_done_any = TRUE;
7994 return change_done_any;
7997 static boolean CheckElementChangeExt(int x, int y,
7999 int trigger_element,
8004 boolean change_done = FALSE;
8007 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8008 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8011 if (Feld[x][y] == EL_BLOCKED)
8013 Blocked2Moving(x, y, &x, &y);
8014 element = Feld[x][y];
8017 if (Feld[x][y] != element) /* check if element has already changed */
8020 for (p = 0; p < element_info[element].num_change_pages; p++)
8022 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8024 boolean check_trigger_element =
8025 (trigger_event == CE_TOUCHING_X ||
8026 trigger_event == CE_HITTING_X ||
8027 trigger_event == CE_HIT_BY_X);
8029 if (change->can_change_or_has_action &&
8030 change->has_event[trigger_event] &&
8031 change->trigger_side & trigger_side &&
8032 change->trigger_player & trigger_player &&
8033 (!check_trigger_element ||
8034 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8036 change->actual_trigger_element = trigger_element;
8037 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8038 change->actual_trigger_side = trigger_side;
8039 change->actual_trigger_ce_value = CustomValue[x][y];
8041 /* special case: trigger element not at (x,y) position for some events */
8042 if (check_trigger_element)
8054 { 0, 0 }, { 0, 0 }, { 0, 0 },
8058 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8059 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8061 change->actual_trigger_ce_value = CustomValue[xx][yy];
8064 if (change->can_change && !change_done)
8066 ChangeDelay[x][y] = 1;
8067 ChangeEvent[x][y] = trigger_event;
8069 HandleElementChange(x, y, p);
8073 #if USE_NEW_DELAYED_ACTION
8074 else if (change->has_action)
8076 ExecuteCustomElementAction(x, y, element, p);
8077 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8080 if (change->has_action)
8082 ExecuteCustomElementAction(x, y, element, p);
8083 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8092 static void PlayPlayerSound(struct PlayerInfo *player)
8094 int jx = player->jx, jy = player->jy;
8095 int sound_element = player->artwork_element;
8096 int last_action = player->last_action_waiting;
8097 int action = player->action_waiting;
8099 if (player->is_waiting)
8101 if (action != last_action)
8102 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8104 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8108 if (action != last_action)
8109 StopSound(element_info[sound_element].sound[last_action]);
8111 if (last_action == ACTION_SLEEPING)
8112 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8116 static void PlayAllPlayersSound()
8120 for (i = 0; i < MAX_PLAYERS; i++)
8121 if (stored_player[i].active)
8122 PlayPlayerSound(&stored_player[i]);
8125 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8127 boolean last_waiting = player->is_waiting;
8128 int move_dir = player->MovDir;
8130 player->last_action_waiting = player->action_waiting;
8134 if (!last_waiting) /* not waiting -> waiting */
8136 player->is_waiting = TRUE;
8138 player->frame_counter_bored =
8140 game.player_boring_delay_fixed +
8141 SimpleRND(game.player_boring_delay_random);
8142 player->frame_counter_sleeping =
8144 game.player_sleeping_delay_fixed +
8145 SimpleRND(game.player_sleeping_delay_random);
8147 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8150 if (game.player_sleeping_delay_fixed +
8151 game.player_sleeping_delay_random > 0 &&
8152 player->anim_delay_counter == 0 &&
8153 player->post_delay_counter == 0 &&
8154 FrameCounter >= player->frame_counter_sleeping)
8155 player->is_sleeping = TRUE;
8156 else if (game.player_boring_delay_fixed +
8157 game.player_boring_delay_random > 0 &&
8158 FrameCounter >= player->frame_counter_bored)
8159 player->is_bored = TRUE;
8161 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8162 player->is_bored ? ACTION_BORING :
8165 if (player->is_sleeping)
8167 if (player->num_special_action_sleeping > 0)
8169 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8171 int last_special_action = player->special_action_sleeping;
8172 int num_special_action = player->num_special_action_sleeping;
8173 int special_action =
8174 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8175 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8176 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8177 last_special_action + 1 : ACTION_SLEEPING);
8178 int special_graphic =
8179 el_act_dir2img(player->artwork_element, special_action, move_dir);
8181 player->anim_delay_counter =
8182 graphic_info[special_graphic].anim_delay_fixed +
8183 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8184 player->post_delay_counter =
8185 graphic_info[special_graphic].post_delay_fixed +
8186 SimpleRND(graphic_info[special_graphic].post_delay_random);
8188 player->special_action_sleeping = special_action;
8191 if (player->anim_delay_counter > 0)
8193 player->action_waiting = player->special_action_sleeping;
8194 player->anim_delay_counter--;
8196 else if (player->post_delay_counter > 0)
8198 player->post_delay_counter--;
8202 else if (player->is_bored)
8204 if (player->num_special_action_bored > 0)
8206 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8208 int special_action =
8209 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8210 int special_graphic =
8211 el_act_dir2img(player->artwork_element, special_action, move_dir);
8213 player->anim_delay_counter =
8214 graphic_info[special_graphic].anim_delay_fixed +
8215 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8216 player->post_delay_counter =
8217 graphic_info[special_graphic].post_delay_fixed +
8218 SimpleRND(graphic_info[special_graphic].post_delay_random);
8220 player->special_action_bored = special_action;
8223 if (player->anim_delay_counter > 0)
8225 player->action_waiting = player->special_action_bored;
8226 player->anim_delay_counter--;
8228 else if (player->post_delay_counter > 0)
8230 player->post_delay_counter--;
8235 else if (last_waiting) /* waiting -> not waiting */
8237 player->is_waiting = FALSE;
8238 player->is_bored = FALSE;
8239 player->is_sleeping = FALSE;
8241 player->frame_counter_bored = -1;
8242 player->frame_counter_sleeping = -1;
8244 player->anim_delay_counter = 0;
8245 player->post_delay_counter = 0;
8247 player->action_waiting = ACTION_DEFAULT;
8249 player->special_action_bored = ACTION_DEFAULT;
8250 player->special_action_sleeping = ACTION_DEFAULT;
8254 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8256 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8257 int left = player_action & JOY_LEFT;
8258 int right = player_action & JOY_RIGHT;
8259 int up = player_action & JOY_UP;
8260 int down = player_action & JOY_DOWN;
8261 int button1 = player_action & JOY_BUTTON_1;
8262 int button2 = player_action & JOY_BUTTON_2;
8263 int dx = (left ? -1 : right ? 1 : 0);
8264 int dy = (up ? -1 : down ? 1 : 0);
8266 if (!player->active || tape.pausing)
8272 snapped = SnapField(player, dx, dy);
8276 dropped = DropElement(player);
8278 moved = MovePlayer(player, dx, dy);
8281 if (tape.single_step && tape.recording && !tape.pausing)
8283 if (button1 || (dropped && !moved))
8285 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8286 SnapField(player, 0, 0); /* stop snapping */
8290 SetPlayerWaiting(player, FALSE);
8292 return player_action;
8296 /* no actions for this player (no input at player's configured device) */
8298 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8299 SnapField(player, 0, 0);
8300 CheckGravityMovementWhenNotMoving(player);
8302 if (player->MovPos == 0)
8303 SetPlayerWaiting(player, TRUE);
8305 if (player->MovPos == 0) /* needed for tape.playing */
8306 player->is_moving = FALSE;
8308 player->is_dropping = FALSE;
8314 void AdvanceFrameAndPlayerCounters(int player_nr)
8318 /* advance frame counters (global frame counter and time frame counter) */
8322 /* advance player counters (counters for move delay, move animation etc.) */
8323 for (i = 0; i < MAX_PLAYERS; i++)
8325 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8326 int move_delay_value = stored_player[i].move_delay_value;
8327 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8329 if (!advance_player_counters) /* not all players may be affected */
8332 #if USE_NEW_PLAYER_ANIM
8333 if (move_frames == 0) /* less than one move per game frame */
8335 int stepsize = TILEX / move_delay_value;
8336 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8337 int count = (stored_player[i].is_moving ?
8338 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8340 if (count % delay == 0)
8345 stored_player[i].Frame += move_frames;
8347 if (stored_player[i].MovPos != 0)
8348 stored_player[i].StepFrame += move_frames;
8350 if (stored_player[i].move_delay > 0)
8351 stored_player[i].move_delay--;
8353 /* due to bugs in previous versions, counter must count up, not down */
8354 if (stored_player[i].push_delay != -1)
8355 stored_player[i].push_delay++;
8357 if (stored_player[i].drop_delay > 0)
8358 stored_player[i].drop_delay--;
8364 static unsigned long game_frame_delay = 0;
8365 unsigned long game_frame_delay_value;
8366 int magic_wall_x = 0, magic_wall_y = 0;
8367 int i, x, y, element, graphic;
8368 byte *recorded_player_action;
8369 byte summarized_player_action = 0;
8370 byte tape_action[MAX_PLAYERS];
8372 if (game_status != GAME_MODE_PLAYING)
8375 game_frame_delay_value =
8376 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8378 if (tape.playing && tape.warp_forward && !tape.pausing)
8379 game_frame_delay_value = 0;
8381 /* ---------- main game synchronization point ---------- */
8383 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8385 InitPlayfieldScanModeVars();
8387 #if USE_ONE_MORE_CHANGE_PER_FRAME
8388 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8390 SCAN_PLAYFIELD(x, y)
8392 ChangeCount[x][y] = 0;
8393 ChangeEvent[x][y] = -1;
8398 if (network_playing && !network_player_action_received)
8400 /* try to get network player actions in time */
8402 #if defined(NETWORK_AVALIABLE)
8403 /* last chance to get network player actions without main loop delay */
8407 /* game was quit by network peer */
8408 if (game_status != GAME_MODE_PLAYING)
8411 if (!network_player_action_received)
8412 return; /* failed to get network player actions in time */
8418 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8421 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8422 if (recorded_player_action == NULL && tape.pausing)
8426 for (i = 0; i < MAX_PLAYERS; i++)
8428 summarized_player_action |= stored_player[i].action;
8430 if (!network_playing)
8431 stored_player[i].effective_action = stored_player[i].action;
8434 #if defined(NETWORK_AVALIABLE)
8435 if (network_playing)
8436 SendToServer_MovePlayer(summarized_player_action);
8439 if (!options.network && !setup.team_mode)
8440 local_player->effective_action = summarized_player_action;
8442 if (recorded_player_action != NULL)
8443 for (i = 0; i < MAX_PLAYERS; i++)
8444 stored_player[i].effective_action = recorded_player_action[i];
8446 for (i = 0; i < MAX_PLAYERS; i++)
8448 tape_action[i] = stored_player[i].effective_action;
8450 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8451 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8454 /* only save actions from input devices, but not programmed actions */
8456 TapeRecordAction(tape_action);
8458 for (i = 0; i < MAX_PLAYERS; i++)
8460 int actual_player_action = stored_player[i].effective_action;
8463 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8464 - rnd_equinox_tetrachloride 048
8465 - rnd_equinox_tetrachloride_ii 096
8466 - rnd_emanuel_schmieg 002
8467 - doctor_sloan_ww 001, 020
8469 if (stored_player[i].MovPos == 0)
8470 CheckGravityMovement(&stored_player[i]);
8473 /* overwrite programmed action with tape action */
8474 if (stored_player[i].programmed_action)
8475 actual_player_action = stored_player[i].programmed_action;
8478 PlayerActions(&stored_player[i], actual_player_action);
8480 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8482 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8483 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8486 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8489 network_player_action_received = FALSE;
8491 ScrollScreen(NULL, SCROLL_GO_ON);
8493 /* for backwards compatibility, the following code emulates a fixed bug that
8494 occured when pushing elements (causing elements that just made their last
8495 pushing step to already (if possible) make their first falling step in the
8496 same game frame, which is bad); this code is also needed to use the famous
8497 "spring push bug" which is used in older levels and might be wanted to be
8498 used also in newer levels, but in this case the buggy pushing code is only
8499 affecting the "spring" element and no other elements */
8501 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8503 for (i = 0; i < MAX_PLAYERS; i++)
8505 struct PlayerInfo *player = &stored_player[i];
8509 if (player->active && player->is_pushing && player->is_moving &&
8511 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8512 Feld[x][y] == EL_SPRING))
8514 ContinueMoving(x, y);
8516 /* continue moving after pushing (this is actually a bug) */
8517 if (!IS_MOVING(x, y))
8526 SCAN_PLAYFIELD(x, y)
8528 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8531 ChangeCount[x][y] = 0;
8532 ChangeEvent[x][y] = -1;
8534 /* this must be handled before main playfield loop */
8535 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8538 if (MovDelay[x][y] <= 0)
8542 #if USE_NEW_SNAP_DELAY
8543 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8546 if (MovDelay[x][y] <= 0)
8549 DrawLevelField(x, y);
8551 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8557 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8559 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8560 printf("GameActions(): This should never happen!\n");
8562 ChangePage[x][y] = -1;
8567 if (WasJustMoving[x][y] > 0)
8568 WasJustMoving[x][y]--;
8569 if (WasJustFalling[x][y] > 0)
8570 WasJustFalling[x][y]--;
8571 if (CheckCollision[x][y] > 0)
8572 CheckCollision[x][y]--;
8576 /* reset finished pushing action (not done in ContinueMoving() to allow
8577 continuous pushing animation for elements with zero push delay) */
8578 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8580 ResetGfxAnimation(x, y);
8581 DrawLevelField(x, y);
8585 if (IS_BLOCKED(x, y))
8589 Blocked2Moving(x, y, &oldx, &oldy);
8590 if (!IS_MOVING(oldx, oldy))
8592 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8593 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8594 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8595 printf("GameActions(): This should never happen!\n");
8602 SCAN_PLAYFIELD(x, y)
8604 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8607 element = Feld[x][y];
8608 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8611 printf("::: %d,%d\n", x, y);
8613 if (element == EL_ROCK)
8614 printf("::: Yo man! Rocks can fall!\n");
8617 if (graphic_info[graphic].anim_global_sync)
8618 GfxFrame[x][y] = FrameCounter;
8619 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8621 int old_gfx_frame = GfxFrame[x][y];
8623 GfxFrame[x][y] = CustomValue[x][y];
8626 if (GfxFrame[x][y] != old_gfx_frame)
8628 DrawLevelGraphicAnimation(x, y, graphic);
8630 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8632 int old_gfx_frame = GfxFrame[x][y];
8634 GfxFrame[x][y] = element_info[element].collect_score;
8637 if (GfxFrame[x][y] != old_gfx_frame)
8639 DrawLevelGraphicAnimation(x, y, graphic);
8642 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8643 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8644 ResetRandomAnimationValue(x, y);
8646 SetRandomAnimationValue(x, y);
8648 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8650 if (IS_INACTIVE(element))
8652 if (IS_ANIMATED(graphic))
8653 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8658 /* this may take place after moving, so 'element' may have changed */
8659 if (IS_CHANGING(x, y) &&
8660 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8662 int page = element_info[element].event_page_nr[CE_DELAY];
8664 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8668 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8672 if (element == EL_CUSTOM_255)
8673 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8677 HandleElementChange(x, y, page);
8679 if (CAN_CHANGE(element))
8680 HandleElementChange(x, y, page);
8682 if (HAS_ACTION(element))
8683 ExecuteCustomElementAction(x, y, element, page);
8688 element = Feld[x][y];
8689 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8692 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8696 element = Feld[x][y];
8697 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8699 if (IS_ANIMATED(graphic) &&
8702 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8704 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8705 EdelsteinFunkeln(x, y);
8707 else if ((element == EL_ACID ||
8708 element == EL_EXIT_OPEN ||
8709 element == EL_SP_EXIT_OPEN ||
8710 element == EL_SP_TERMINAL ||
8711 element == EL_SP_TERMINAL_ACTIVE ||
8712 element == EL_EXTRA_TIME ||
8713 element == EL_SHIELD_NORMAL ||
8714 element == EL_SHIELD_DEADLY) &&
8715 IS_ANIMATED(graphic))
8716 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8717 else if (IS_MOVING(x, y))
8718 ContinueMoving(x, y);
8719 else if (IS_ACTIVE_BOMB(element))
8720 CheckDynamite(x, y);
8721 else if (element == EL_AMOEBA_GROWING)
8722 AmoebeWaechst(x, y);
8723 else if (element == EL_AMOEBA_SHRINKING)
8724 AmoebaDisappearing(x, y);
8726 #if !USE_NEW_AMOEBA_CODE
8727 else if (IS_AMOEBALIVE(element))
8728 AmoebeAbleger(x, y);
8731 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8733 else if (element == EL_EXIT_CLOSED)
8735 else if (element == EL_SP_EXIT_CLOSED)
8737 else if (element == EL_EXPANDABLE_WALL_GROWING)
8739 else if (element == EL_EXPANDABLE_WALL ||
8740 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8741 element == EL_EXPANDABLE_WALL_VERTICAL ||
8742 element == EL_EXPANDABLE_WALL_ANY)
8744 else if (element == EL_FLAMES)
8745 CheckForDragon(x, y);
8746 else if (element == EL_EXPLOSION)
8747 ; /* drawing of correct explosion animation is handled separately */
8748 else if (element == EL_ELEMENT_SNAPPING)
8751 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8753 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8756 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8757 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8760 if (element == EL_CUSTOM_255 ||
8761 element == EL_CUSTOM_256)
8762 DrawLevelGraphicAnimation(x, y, graphic);
8765 if (IS_BELT_ACTIVE(element))
8766 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8768 if (game.magic_wall_active)
8770 int jx = local_player->jx, jy = local_player->jy;
8772 /* play the element sound at the position nearest to the player */
8773 if ((element == EL_MAGIC_WALL_FULL ||
8774 element == EL_MAGIC_WALL_ACTIVE ||
8775 element == EL_MAGIC_WALL_EMPTYING ||
8776 element == EL_BD_MAGIC_WALL_FULL ||
8777 element == EL_BD_MAGIC_WALL_ACTIVE ||
8778 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8779 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8787 #if USE_NEW_AMOEBA_CODE
8788 /* new experimental amoeba growth stuff */
8789 if (!(FrameCounter % 8))
8791 static unsigned long random = 1684108901;
8793 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8795 x = RND(lev_fieldx);
8796 y = RND(lev_fieldy);
8797 element = Feld[x][y];
8799 if (!IS_PLAYER(x,y) &&
8800 (element == EL_EMPTY ||
8801 CAN_GROW_INTO(element) ||
8802 element == EL_QUICKSAND_EMPTY ||
8803 element == EL_ACID_SPLASH_LEFT ||
8804 element == EL_ACID_SPLASH_RIGHT))
8806 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8807 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8808 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8809 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8810 Feld[x][y] = EL_AMOEBA_DROP;
8813 random = random * 129 + 1;
8819 if (game.explosions_delayed)
8822 game.explosions_delayed = FALSE;
8825 SCAN_PLAYFIELD(x, y)
8827 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8830 element = Feld[x][y];
8832 if (ExplodeField[x][y])
8833 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8834 else if (element == EL_EXPLOSION)
8835 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8837 ExplodeField[x][y] = EX_TYPE_NONE;
8840 game.explosions_delayed = TRUE;
8843 if (game.magic_wall_active)
8845 if (!(game.magic_wall_time_left % 4))
8847 int element = Feld[magic_wall_x][magic_wall_y];
8849 if (element == EL_BD_MAGIC_WALL_FULL ||
8850 element == EL_BD_MAGIC_WALL_ACTIVE ||
8851 element == EL_BD_MAGIC_WALL_EMPTYING)
8852 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8854 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8857 if (game.magic_wall_time_left > 0)
8859 game.magic_wall_time_left--;
8860 if (!game.magic_wall_time_left)
8863 SCAN_PLAYFIELD(x, y)
8865 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8868 element = Feld[x][y];
8870 if (element == EL_MAGIC_WALL_ACTIVE ||
8871 element == EL_MAGIC_WALL_FULL)
8873 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8874 DrawLevelField(x, y);
8876 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8877 element == EL_BD_MAGIC_WALL_FULL)
8879 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8880 DrawLevelField(x, y);
8884 game.magic_wall_active = FALSE;
8889 if (game.light_time_left > 0)
8891 game.light_time_left--;
8893 if (game.light_time_left == 0)
8894 RedrawAllLightSwitchesAndInvisibleElements();
8897 if (game.timegate_time_left > 0)
8899 game.timegate_time_left--;
8901 if (game.timegate_time_left == 0)
8902 CloseAllOpenTimegates();
8905 if (game.lenses_time_left > 0)
8907 game.lenses_time_left--;
8909 if (game.lenses_time_left == 0)
8910 RedrawAllInvisibleElementsForLenses();
8913 if (game.magnify_time_left > 0)
8915 game.magnify_time_left--;
8917 if (game.magnify_time_left == 0)
8918 RedrawAllInvisibleElementsForMagnifier();
8921 for (i = 0; i < MAX_PLAYERS; i++)
8923 struct PlayerInfo *player = &stored_player[i];
8925 if (SHIELD_ON(player))
8927 if (player->shield_deadly_time_left)
8928 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8929 else if (player->shield_normal_time_left)
8930 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8934 if (TimeFrames >= FRAMES_PER_SECOND)
8939 for (i = 0; i < MAX_PLAYERS; i++)
8941 struct PlayerInfo *player = &stored_player[i];
8943 if (SHIELD_ON(player))
8945 player->shield_normal_time_left--;
8947 if (player->shield_deadly_time_left > 0)
8948 player->shield_deadly_time_left--;
8952 if (!level.use_step_counter)
8960 if (TimeLeft <= 10 && setup.time_limit)
8961 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8963 DrawGameValue_Time(TimeLeft);
8965 if (!TimeLeft && setup.time_limit)
8966 for (i = 0; i < MAX_PLAYERS; i++)
8967 KillPlayer(&stored_player[i]);
8969 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8970 DrawGameValue_Time(TimePlayed);
8973 if (tape.recording || tape.playing)
8974 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8978 PlayAllPlayersSound();
8980 if (options.debug) /* calculate frames per second */
8982 static unsigned long fps_counter = 0;
8983 static int fps_frames = 0;
8984 unsigned long fps_delay_ms = Counter() - fps_counter;
8988 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8990 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8993 fps_counter = Counter();
8996 redraw_mask |= REDRAW_FPS;
8999 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9001 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9003 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9005 local_player->show_envelope = 0;
9008 /* use random number generator in every frame to make it less predictable */
9009 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9013 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9015 int min_x = x, min_y = y, max_x = x, max_y = y;
9018 for (i = 0; i < MAX_PLAYERS; i++)
9020 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9022 if (!stored_player[i].active || &stored_player[i] == player)
9025 min_x = MIN(min_x, jx);
9026 min_y = MIN(min_y, jy);
9027 max_x = MAX(max_x, jx);
9028 max_y = MAX(max_y, jy);
9031 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9034 static boolean AllPlayersInVisibleScreen()
9038 for (i = 0; i < MAX_PLAYERS; i++)
9040 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9042 if (!stored_player[i].active)
9045 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9052 void ScrollLevel(int dx, int dy)
9054 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9057 BlitBitmap(drawto_field, drawto_field,
9058 FX + TILEX * (dx == -1) - softscroll_offset,
9059 FY + TILEY * (dy == -1) - softscroll_offset,
9060 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9061 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9062 FX + TILEX * (dx == 1) - softscroll_offset,
9063 FY + TILEY * (dy == 1) - softscroll_offset);
9067 x = (dx == 1 ? BX1 : BX2);
9068 for (y = BY1; y <= BY2; y++)
9069 DrawScreenField(x, y);
9074 y = (dy == 1 ? BY1 : BY2);
9075 for (x = BX1; x <= BX2; x++)
9076 DrawScreenField(x, y);
9079 redraw_mask |= REDRAW_FIELD;
9082 static boolean canFallDown(struct PlayerInfo *player)
9084 int jx = player->jx, jy = player->jy;
9086 return (IN_LEV_FIELD(jx, jy + 1) &&
9087 (IS_FREE(jx, jy + 1) ||
9088 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9089 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9090 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9093 static boolean canPassField(int x, int y, int move_dir)
9095 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9096 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9097 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9100 int element = Feld[x][y];
9102 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9103 !CAN_MOVE(element) &&
9104 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9105 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9106 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9109 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9111 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9112 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9113 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9117 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9118 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9119 (IS_DIGGABLE(Feld[newx][newy]) ||
9120 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9121 canPassField(newx, newy, move_dir)));
9124 static void CheckGravityMovement(struct PlayerInfo *player)
9126 if (game.gravity && !player->programmed_action)
9128 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9129 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9130 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9131 int jx = player->jx, jy = player->jy;
9132 boolean player_is_moving_to_valid_field =
9133 (!player_is_snapping &&
9134 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9135 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9136 boolean player_can_fall_down = canFallDown(player);
9138 if (player_can_fall_down &&
9139 !player_is_moving_to_valid_field)
9140 player->programmed_action = MV_DOWN;
9144 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9146 return CheckGravityMovement(player);
9148 if (game.gravity && !player->programmed_action)
9150 int jx = player->jx, jy = player->jy;
9151 boolean field_under_player_is_free =
9152 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9153 boolean player_is_standing_on_valid_field =
9154 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9155 (IS_WALKABLE(Feld[jx][jy]) &&
9156 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9158 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9159 player->programmed_action = MV_DOWN;
9165 -----------------------------------------------------------------------------
9166 dx, dy: direction (non-diagonal) to try to move the player to
9167 real_dx, real_dy: direction as read from input device (can be diagonal)
9170 boolean MovePlayerOneStep(struct PlayerInfo *player,
9171 int dx, int dy, int real_dx, int real_dy)
9173 int jx = player->jx, jy = player->jy;
9174 int new_jx = jx + dx, new_jy = jy + dy;
9175 #if !USE_FIXED_DONT_RUN_INTO
9179 boolean player_can_move = !player->cannot_move;
9181 if (!player->active || (!dx && !dy))
9182 return MP_NO_ACTION;
9184 player->MovDir = (dx < 0 ? MV_LEFT :
9187 dy > 0 ? MV_DOWN : MV_NONE);
9189 if (!IN_LEV_FIELD(new_jx, new_jy))
9190 return MP_NO_ACTION;
9192 if (!player_can_move)
9195 if (player->MovPos == 0)
9197 player->is_moving = FALSE;
9198 player->is_digging = FALSE;
9199 player->is_collecting = FALSE;
9200 player->is_snapping = FALSE;
9201 player->is_pushing = FALSE;
9204 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9205 SnapField(player, 0, 0);
9209 return MP_NO_ACTION;
9213 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9214 return MP_NO_ACTION;
9216 #if !USE_FIXED_DONT_RUN_INTO
9217 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9219 /* (moved to DigField()) */
9220 if (player_can_move && DONT_RUN_INTO(element))
9222 if (element == EL_ACID && dx == 0 && dy == 1)
9224 SplashAcid(new_jx, new_jy);
9225 Feld[jx][jy] = EL_PLAYER_1;
9226 InitMovingField(jx, jy, MV_DOWN);
9227 Store[jx][jy] = EL_ACID;
9228 ContinueMoving(jx, jy);
9232 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9238 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9240 #if USE_FIXED_DONT_RUN_INTO
9241 if (can_move == MP_DONT_RUN_INTO)
9245 if (can_move != MP_MOVING)
9248 #if USE_FIXED_DONT_RUN_INTO
9251 /* check if DigField() has caused relocation of the player */
9252 if (player->jx != jx || player->jy != jy)
9253 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9255 StorePlayer[jx][jy] = 0;
9256 player->last_jx = jx;
9257 player->last_jy = jy;
9258 player->jx = new_jx;
9259 player->jy = new_jy;
9260 StorePlayer[new_jx][new_jy] = player->element_nr;
9262 if (player->move_delay_value_next != -1)
9264 player->move_delay_value = player->move_delay_value_next;
9265 player->move_delay_value_next = -1;
9269 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9271 player->step_counter++;
9273 PlayerVisit[jx][jy] = FrameCounter;
9275 ScrollPlayer(player, SCROLL_INIT);
9280 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9282 int jx = player->jx, jy = player->jy;
9283 int old_jx = jx, old_jy = jy;
9284 int moved = MP_NO_ACTION;
9286 if (!player->active)
9291 if (player->MovPos == 0)
9293 player->is_moving = FALSE;
9294 player->is_digging = FALSE;
9295 player->is_collecting = FALSE;
9296 player->is_snapping = FALSE;
9297 player->is_pushing = FALSE;
9303 if (player->move_delay > 0)
9306 player->move_delay = -1; /* set to "uninitialized" value */
9308 /* store if player is automatically moved to next field */
9309 player->is_auto_moving = (player->programmed_action != MV_NONE);
9311 /* remove the last programmed player action */
9312 player->programmed_action = 0;
9316 /* should only happen if pre-1.2 tape recordings are played */
9317 /* this is only for backward compatibility */
9319 int original_move_delay_value = player->move_delay_value;
9322 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9326 /* scroll remaining steps with finest movement resolution */
9327 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9329 while (player->MovPos)
9331 ScrollPlayer(player, SCROLL_GO_ON);
9332 ScrollScreen(NULL, SCROLL_GO_ON);
9334 AdvanceFrameAndPlayerCounters(player->index_nr);
9340 player->move_delay_value = original_move_delay_value;
9343 if (player->last_move_dir & MV_HORIZONTAL)
9345 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9346 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9350 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9351 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9357 if (moved & MP_MOVING && !ScreenMovPos &&
9358 (player == local_player || !options.network))
9360 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9361 int offset = (setup.scroll_delay ? 3 : 0);
9363 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9365 /* actual player has left the screen -- scroll in that direction */
9366 if (jx != old_jx) /* player has moved horizontally */
9367 scroll_x += (jx - old_jx);
9368 else /* player has moved vertically */
9369 scroll_y += (jy - old_jy);
9373 if (jx != old_jx) /* player has moved horizontally */
9375 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9376 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9377 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9379 /* don't scroll over playfield boundaries */
9380 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9381 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9383 /* don't scroll more than one field at a time */
9384 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9386 /* don't scroll against the player's moving direction */
9387 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9388 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9389 scroll_x = old_scroll_x;
9391 else /* player has moved vertically */
9393 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9394 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9395 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9397 /* don't scroll over playfield boundaries */
9398 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9399 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9401 /* don't scroll more than one field at a time */
9402 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9404 /* don't scroll against the player's moving direction */
9405 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9406 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9407 scroll_y = old_scroll_y;
9411 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9413 if (!options.network && !AllPlayersInVisibleScreen())
9415 scroll_x = old_scroll_x;
9416 scroll_y = old_scroll_y;
9420 ScrollScreen(player, SCROLL_INIT);
9421 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9426 player->StepFrame = 0;
9428 if (moved & MP_MOVING)
9430 if (old_jx != jx && old_jy == jy)
9431 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9432 else if (old_jx == jx && old_jy != jy)
9433 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9435 DrawLevelField(jx, jy); /* for "crumbled sand" */
9437 player->last_move_dir = player->MovDir;
9438 player->is_moving = TRUE;
9439 player->is_snapping = FALSE;
9440 player->is_switching = FALSE;
9441 player->is_dropping = FALSE;
9445 CheckGravityMovementWhenNotMoving(player);
9447 player->is_moving = FALSE;
9449 /* at this point, the player is allowed to move, but cannot move right now
9450 (e.g. because of something blocking the way) -- ensure that the player
9451 is also allowed to move in the next frame (in old versions before 3.1.1,
9452 the player was forced to wait again for eight frames before next try) */
9454 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9455 player->move_delay = 0; /* allow direct movement in the next frame */
9458 if (player->move_delay == -1) /* not yet initialized by DigField() */
9459 player->move_delay = player->move_delay_value;
9461 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9463 TestIfPlayerTouchesBadThing(jx, jy);
9464 TestIfPlayerTouchesCustomElement(jx, jy);
9467 if (!player->active)
9468 RemovePlayer(player);
9473 void ScrollPlayer(struct PlayerInfo *player, int mode)
9475 int jx = player->jx, jy = player->jy;
9476 int last_jx = player->last_jx, last_jy = player->last_jy;
9477 int move_stepsize = TILEX / player->move_delay_value;
9479 #if USE_NEW_PLAYER_SPEED
9480 if (!player->active)
9483 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9486 if (!player->active || player->MovPos == 0)
9490 if (mode == SCROLL_INIT)
9492 player->actual_frame_counter = FrameCounter;
9493 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9495 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9496 Feld[last_jx][last_jy] == EL_EMPTY)
9498 int last_field_block_delay = 0; /* start with no blocking at all */
9499 int block_delay_adjustment = player->block_delay_adjustment;
9501 /* if player blocks last field, add delay for exactly one move */
9502 if (player->block_last_field)
9504 last_field_block_delay += player->move_delay_value;
9506 /* when blocking enabled, prevent moving up despite gravity */
9507 if (game.gravity && player->MovDir == MV_UP)
9508 block_delay_adjustment = -1;
9511 /* add block delay adjustment (also possible when not blocking) */
9512 last_field_block_delay += block_delay_adjustment;
9514 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9515 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9518 #if USE_NEW_PLAYER_SPEED
9519 if (player->MovPos != 0) /* player has not yet reached destination */
9525 else if (!FrameReached(&player->actual_frame_counter, 1))
9529 printf("::: player->MovPos: %d -> %d\n",
9531 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9534 #if USE_NEW_PLAYER_SPEED
9535 if (player->MovPos != 0)
9537 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9538 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9540 /* before DrawPlayer() to draw correct player graphic for this case */
9541 if (player->MovPos == 0)
9542 CheckGravityMovement(player);
9545 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9546 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9548 /* before DrawPlayer() to draw correct player graphic for this case */
9549 if (player->MovPos == 0)
9550 CheckGravityMovement(player);
9553 if (player->MovPos == 0) /* player reached destination field */
9556 printf("::: player reached destination field\n");
9559 if (player->move_delay_reset_counter > 0)
9561 player->move_delay_reset_counter--;
9563 if (player->move_delay_reset_counter == 0)
9565 /* continue with normal speed after quickly moving through gate */
9566 HALVE_PLAYER_SPEED(player);
9568 /* be able to make the next move without delay */
9569 player->move_delay = 0;
9573 player->last_jx = jx;
9574 player->last_jy = jy;
9576 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9577 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9578 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9580 DrawPlayer(player); /* needed here only to cleanup last field */
9581 RemovePlayer(player);
9583 if (local_player->friends_still_needed == 0 ||
9584 IS_SP_ELEMENT(Feld[jx][jy]))
9585 player->LevelSolved = player->GameOver = TRUE;
9588 /* this breaks one level: "machine", level 000 */
9590 int move_direction = player->MovDir;
9591 int enter_side = MV_DIR_OPPOSITE(move_direction);
9592 int leave_side = move_direction;
9593 int old_jx = last_jx;
9594 int old_jy = last_jy;
9595 int old_element = Feld[old_jx][old_jy];
9596 int new_element = Feld[jx][jy];
9598 if (IS_CUSTOM_ELEMENT(old_element))
9599 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9601 player->index_bit, leave_side);
9603 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9605 player->index_bit, leave_side);
9607 if (IS_CUSTOM_ELEMENT(new_element))
9608 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9609 player->index_bit, enter_side);
9611 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9613 player->index_bit, enter_side);
9615 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9616 CE_MOVE_OF_X, move_direction);
9619 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9621 TestIfPlayerTouchesBadThing(jx, jy);
9622 TestIfPlayerTouchesCustomElement(jx, jy);
9624 /* needed because pushed element has not yet reached its destination,
9625 so it would trigger a change event at its previous field location */
9626 if (!player->is_pushing)
9627 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9629 if (!player->active)
9630 RemovePlayer(player);
9633 if (level.use_step_counter)
9643 if (TimeLeft <= 10 && setup.time_limit)
9644 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9646 DrawGameValue_Time(TimeLeft);
9648 if (!TimeLeft && setup.time_limit)
9649 for (i = 0; i < MAX_PLAYERS; i++)
9650 KillPlayer(&stored_player[i]);
9652 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9653 DrawGameValue_Time(TimePlayed);
9656 if (tape.single_step && tape.recording && !tape.pausing &&
9657 !player->programmed_action)
9658 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9662 void ScrollScreen(struct PlayerInfo *player, int mode)
9664 static unsigned long screen_frame_counter = 0;
9666 if (mode == SCROLL_INIT)
9668 /* set scrolling step size according to actual player's moving speed */
9669 ScrollStepSize = TILEX / player->move_delay_value;
9671 screen_frame_counter = FrameCounter;
9672 ScreenMovDir = player->MovDir;
9673 ScreenMovPos = player->MovPos;
9674 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9677 else if (!FrameReached(&screen_frame_counter, 1))
9682 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9683 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9684 redraw_mask |= REDRAW_FIELD;
9687 ScreenMovDir = MV_NONE;
9690 void TestIfPlayerTouchesCustomElement(int x, int y)
9692 static int xy[4][2] =
9699 static int trigger_sides[4][2] =
9701 /* center side border side */
9702 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9703 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9704 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9705 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9707 static int touch_dir[4] =
9714 int center_element = Feld[x][y]; /* should always be non-moving! */
9717 for (i = 0; i < NUM_DIRECTIONS; i++)
9719 int xx = x + xy[i][0];
9720 int yy = y + xy[i][1];
9721 int center_side = trigger_sides[i][0];
9722 int border_side = trigger_sides[i][1];
9725 if (!IN_LEV_FIELD(xx, yy))
9728 if (IS_PLAYER(x, y))
9730 struct PlayerInfo *player = PLAYERINFO(x, y);
9732 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9733 border_element = Feld[xx][yy]; /* may be moving! */
9734 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9735 border_element = Feld[xx][yy];
9736 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9737 border_element = MovingOrBlocked2Element(xx, yy);
9739 continue; /* center and border element do not touch */
9741 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9742 player->index_bit, border_side);
9743 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9744 CE_PLAYER_TOUCHES_X,
9745 player->index_bit, border_side);
9747 else if (IS_PLAYER(xx, yy))
9749 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9751 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9753 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9754 continue; /* center and border element do not touch */
9757 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9758 player->index_bit, center_side);
9759 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9760 CE_PLAYER_TOUCHES_X,
9761 player->index_bit, center_side);
9767 void TestIfElementTouchesCustomElement(int x, int y)
9769 static int xy[4][2] =
9776 static int trigger_sides[4][2] =
9778 /* center side border side */
9779 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9780 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9781 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9782 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9784 static int touch_dir[4] =
9791 boolean change_center_element = FALSE;
9792 int center_element = Feld[x][y]; /* should always be non-moving! */
9795 for (i = 0; i < NUM_DIRECTIONS; i++)
9797 int xx = x + xy[i][0];
9798 int yy = y + xy[i][1];
9799 int center_side = trigger_sides[i][0];
9800 int border_side = trigger_sides[i][1];
9803 if (!IN_LEV_FIELD(xx, yy))
9806 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9807 border_element = Feld[xx][yy]; /* may be moving! */
9808 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9809 border_element = Feld[xx][yy];
9810 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9811 border_element = MovingOrBlocked2Element(xx, yy);
9813 continue; /* center and border element do not touch */
9815 /* check for change of center element (but change it only once) */
9816 if (!change_center_element)
9817 change_center_element =
9818 CheckElementChangeBySide(x, y, center_element, border_element,
9819 CE_TOUCHING_X, border_side);
9821 /* check for change of border element */
9822 CheckElementChangeBySide(xx, yy, border_element, center_element,
9823 CE_TOUCHING_X, center_side);
9827 void TestIfElementHitsCustomElement(int x, int y, int direction)
9829 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9830 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9831 int hitx = x + dx, hity = y + dy;
9832 int hitting_element = Feld[x][y];
9833 int touched_element;
9835 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9838 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9839 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9841 if (IN_LEV_FIELD(hitx, hity))
9843 int opposite_direction = MV_DIR_OPPOSITE(direction);
9844 int hitting_side = direction;
9845 int touched_side = opposite_direction;
9846 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9847 MovDir[hitx][hity] != direction ||
9848 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9854 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9855 CE_HITTING_X, touched_side);
9857 CheckElementChangeBySide(hitx, hity, touched_element,
9858 hitting_element, CE_HIT_BY_X, hitting_side);
9860 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9861 CE_HIT_BY_SOMETHING, opposite_direction);
9865 /* "hitting something" is also true when hitting the playfield border */
9866 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9867 CE_HITTING_SOMETHING, direction);
9871 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9873 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9874 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9875 int hitx = x + dx, hity = y + dy;
9876 int hitting_element = Feld[x][y];
9877 int touched_element;
9879 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9880 !IS_FREE(hitx, hity) &&
9881 (!IS_MOVING(hitx, hity) ||
9882 MovDir[hitx][hity] != direction ||
9883 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9886 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9890 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9894 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9895 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9897 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9898 EP_CAN_SMASH_EVERYTHING, direction);
9900 if (IN_LEV_FIELD(hitx, hity))
9902 int opposite_direction = MV_DIR_OPPOSITE(direction);
9903 int hitting_side = direction;
9904 int touched_side = opposite_direction;
9906 int touched_element = MovingOrBlocked2Element(hitx, hity);
9909 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9910 MovDir[hitx][hity] != direction ||
9911 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9920 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9921 CE_SMASHED_BY_SOMETHING, opposite_direction);
9923 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9924 CE_OTHER_IS_SMASHING, touched_side);
9926 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9927 CE_OTHER_GETS_SMASHED, hitting_side);
9933 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9935 int i, kill_x = -1, kill_y = -1;
9937 int bad_element = -1;
9938 static int test_xy[4][2] =
9945 static int test_dir[4] =
9953 for (i = 0; i < NUM_DIRECTIONS; i++)
9955 int test_x, test_y, test_move_dir, test_element;
9957 test_x = good_x + test_xy[i][0];
9958 test_y = good_y + test_xy[i][1];
9960 if (!IN_LEV_FIELD(test_x, test_y))
9964 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9966 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9968 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9969 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9971 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9972 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9976 bad_element = test_element;
9982 if (kill_x != -1 || kill_y != -1)
9984 if (IS_PLAYER(good_x, good_y))
9986 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9988 if (player->shield_deadly_time_left > 0 &&
9989 !IS_INDESTRUCTIBLE(bad_element))
9990 Bang(kill_x, kill_y);
9991 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9995 Bang(good_x, good_y);
9999 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10001 int i, kill_x = -1, kill_y = -1;
10002 int bad_element = Feld[bad_x][bad_y];
10003 static int test_xy[4][2] =
10010 static int touch_dir[4] =
10012 MV_LEFT | MV_RIGHT,
10017 static int test_dir[4] =
10025 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10028 for (i = 0; i < NUM_DIRECTIONS; i++)
10030 int test_x, test_y, test_move_dir, test_element;
10032 test_x = bad_x + test_xy[i][0];
10033 test_y = bad_y + test_xy[i][1];
10034 if (!IN_LEV_FIELD(test_x, test_y))
10038 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10040 test_element = Feld[test_x][test_y];
10042 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10043 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10045 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10046 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10048 /* good thing is player or penguin that does not move away */
10049 if (IS_PLAYER(test_x, test_y))
10051 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10053 if (bad_element == EL_ROBOT && player->is_moving)
10054 continue; /* robot does not kill player if he is moving */
10056 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10058 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10059 continue; /* center and border element do not touch */
10066 else if (test_element == EL_PENGUIN)
10075 if (kill_x != -1 || kill_y != -1)
10077 if (IS_PLAYER(kill_x, kill_y))
10079 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10081 if (player->shield_deadly_time_left > 0 &&
10082 !IS_INDESTRUCTIBLE(bad_element))
10083 Bang(bad_x, bad_y);
10084 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10085 KillPlayer(player);
10088 Bang(kill_x, kill_y);
10092 void TestIfPlayerTouchesBadThing(int x, int y)
10094 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10097 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10099 TestIfGoodThingHitsBadThing(x, y, move_dir);
10102 void TestIfBadThingTouchesPlayer(int x, int y)
10104 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10107 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10109 TestIfBadThingHitsGoodThing(x, y, move_dir);
10112 void TestIfFriendTouchesBadThing(int x, int y)
10114 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10117 void TestIfBadThingTouchesFriend(int x, int y)
10119 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10122 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10124 int i, kill_x = bad_x, kill_y = bad_y;
10125 static int xy[4][2] =
10133 for (i = 0; i < NUM_DIRECTIONS; i++)
10137 x = bad_x + xy[i][0];
10138 y = bad_y + xy[i][1];
10139 if (!IN_LEV_FIELD(x, y))
10142 element = Feld[x][y];
10143 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10144 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10152 if (kill_x != bad_x || kill_y != bad_y)
10153 Bang(bad_x, bad_y);
10156 void KillPlayer(struct PlayerInfo *player)
10158 int jx = player->jx, jy = player->jy;
10160 if (!player->active)
10163 /* remove accessible field at the player's position */
10164 Feld[jx][jy] = EL_EMPTY;
10166 /* deactivate shield (else Bang()/Explode() would not work right) */
10167 player->shield_normal_time_left = 0;
10168 player->shield_deadly_time_left = 0;
10171 BuryPlayer(player);
10174 static void KillPlayerUnlessEnemyProtected(int x, int y)
10176 if (!PLAYER_ENEMY_PROTECTED(x, y))
10177 KillPlayer(PLAYERINFO(x, y));
10180 static void KillPlayerUnlessExplosionProtected(int x, int y)
10182 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10183 KillPlayer(PLAYERINFO(x, y));
10186 void BuryPlayer(struct PlayerInfo *player)
10188 int jx = player->jx, jy = player->jy;
10190 if (!player->active)
10193 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10194 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10196 player->GameOver = TRUE;
10197 RemovePlayer(player);
10200 void RemovePlayer(struct PlayerInfo *player)
10202 int jx = player->jx, jy = player->jy;
10203 int i, found = FALSE;
10205 player->present = FALSE;
10206 player->active = FALSE;
10208 if (!ExplodeField[jx][jy])
10209 StorePlayer[jx][jy] = 0;
10211 if (player->is_moving)
10212 DrawLevelField(player->last_jx, player->last_jy);
10214 for (i = 0; i < MAX_PLAYERS; i++)
10215 if (stored_player[i].active)
10219 AllPlayersGone = TRUE;
10225 #if USE_NEW_SNAP_DELAY
10226 static void setFieldForSnapping(int x, int y, int element, int direction)
10228 struct ElementInfo *ei = &element_info[element];
10229 int direction_bit = MV_DIR_BIT(direction);
10230 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10231 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10232 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10234 Feld[x][y] = EL_ELEMENT_SNAPPING;
10235 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10237 ResetGfxAnimation(x, y);
10239 GfxElement[x][y] = element;
10240 GfxAction[x][y] = action;
10241 GfxDir[x][y] = direction;
10242 GfxFrame[x][y] = -1;
10247 =============================================================================
10248 checkDiagonalPushing()
10249 -----------------------------------------------------------------------------
10250 check if diagonal input device direction results in pushing of object
10251 (by checking if the alternative direction is walkable, diggable, ...)
10252 =============================================================================
10255 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10256 int x, int y, int real_dx, int real_dy)
10258 int jx, jy, dx, dy, xx, yy;
10260 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10263 /* diagonal direction: check alternative direction */
10268 xx = jx + (dx == 0 ? real_dx : 0);
10269 yy = jy + (dy == 0 ? real_dy : 0);
10271 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10275 =============================================================================
10277 -----------------------------------------------------------------------------
10278 x, y: field next to player (non-diagonal) to try to dig to
10279 real_dx, real_dy: direction as read from input device (can be diagonal)
10280 =============================================================================
10283 int DigField(struct PlayerInfo *player,
10284 int oldx, int oldy, int x, int y,
10285 int real_dx, int real_dy, int mode)
10287 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10288 boolean player_was_pushing = player->is_pushing;
10289 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10290 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10291 int jx = oldx, jy = oldy;
10292 int dx = x - jx, dy = y - jy;
10293 int nextx = x + dx, nexty = y + dy;
10294 int move_direction = (dx == -1 ? MV_LEFT :
10295 dx == +1 ? MV_RIGHT :
10297 dy == +1 ? MV_DOWN : MV_NONE);
10298 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10299 int dig_side = MV_DIR_OPPOSITE(move_direction);
10300 int old_element = Feld[jx][jy];
10301 #if USE_FIXED_DONT_RUN_INTO
10302 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10308 if (is_player) /* function can also be called by EL_PENGUIN */
10310 if (player->MovPos == 0)
10312 player->is_digging = FALSE;
10313 player->is_collecting = FALSE;
10316 if (player->MovPos == 0) /* last pushing move finished */
10317 player->is_pushing = FALSE;
10319 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10321 player->is_switching = FALSE;
10322 player->push_delay = -1;
10324 return MP_NO_ACTION;
10328 #if !USE_FIXED_DONT_RUN_INTO
10329 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10330 return MP_NO_ACTION;
10333 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10334 old_element = Back[jx][jy];
10336 /* in case of element dropped at player position, check background */
10337 else if (Back[jx][jy] != EL_EMPTY &&
10338 game.engine_version >= VERSION_IDENT(2,2,0,0))
10339 old_element = Back[jx][jy];
10342 #if USE_FIXED_DONT_RUN_INTO
10343 if (player_can_move && DONT_RUN_INTO(element))
10345 if (element == EL_ACID && dx == 0 && dy == 1)
10348 Feld[jx][jy] = EL_PLAYER_1;
10349 InitMovingField(jx, jy, MV_DOWN);
10350 Store[jx][jy] = EL_ACID;
10351 ContinueMoving(jx, jy);
10352 BuryPlayer(player);
10355 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10357 return MP_DONT_RUN_INTO;
10363 #if USE_FIXED_DONT_RUN_INTO
10364 if (player_can_move && DONT_RUN_INTO(element))
10366 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10368 return MP_DONT_RUN_INTO;
10373 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10374 return MP_NO_ACTION; /* field has no opening in this direction */
10376 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10377 return MP_NO_ACTION; /* field has no opening in this direction */
10380 #if USE_FIXED_DONT_RUN_INTO
10381 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10384 Feld[jx][jy] = EL_PLAYER_1;
10385 InitMovingField(jx, jy, MV_DOWN);
10386 Store[jx][jy] = EL_ACID;
10387 ContinueMoving(jx, jy);
10388 BuryPlayer(player);
10390 return MP_DONT_RUN_INTO;
10396 #if USE_FIXED_DONT_RUN_INTO
10397 if (player_can_move && DONT_RUN_INTO(element))
10399 if (element == EL_ACID && dx == 0 && dy == 1)
10402 Feld[jx][jy] = EL_PLAYER_1;
10403 InitMovingField(jx, jy, MV_DOWN);
10404 Store[jx][jy] = EL_ACID;
10405 ContinueMoving(jx, jy);
10406 BuryPlayer(player);
10409 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10411 return MP_DONT_RUN_INTO;
10416 #if USE_FIXED_DONT_RUN_INTO
10417 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10418 return MP_NO_ACTION;
10421 #if !USE_FIXED_DONT_RUN_INTO
10422 element = Feld[x][y];
10425 collect_count = element_info[element].collect_count_initial;
10427 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10428 return MP_NO_ACTION;
10430 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10431 player_can_move = player_can_move_or_snap;
10433 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10434 game.engine_version >= VERSION_IDENT(2,2,0,0))
10436 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10437 player->index_bit, dig_side);
10438 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10439 player->index_bit, dig_side);
10441 if (Feld[x][y] != element) /* field changed by snapping */
10444 return MP_NO_ACTION;
10447 if (game.gravity && is_player && !player->is_auto_moving &&
10448 canFallDown(player) && move_direction != MV_DOWN &&
10449 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10450 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10452 if (player_can_move &&
10453 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10455 int sound_element = SND_ELEMENT(element);
10456 int sound_action = ACTION_WALKING;
10458 if (IS_RND_GATE(element))
10460 if (!player->key[RND_GATE_NR(element)])
10461 return MP_NO_ACTION;
10463 else if (IS_RND_GATE_GRAY(element))
10465 if (!player->key[RND_GATE_GRAY_NR(element)])
10466 return MP_NO_ACTION;
10468 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10470 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10471 return MP_NO_ACTION;
10473 else if (element == EL_EXIT_OPEN ||
10474 element == EL_SP_EXIT_OPEN ||
10475 element == EL_SP_EXIT_OPENING)
10477 sound_action = ACTION_PASSING; /* player is passing exit */
10479 else if (element == EL_EMPTY)
10481 sound_action = ACTION_MOVING; /* nothing to walk on */
10484 /* play sound from background or player, whatever is available */
10485 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10486 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10488 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10490 else if (player_can_move &&
10491 IS_PASSABLE(element) && canPassField(x, y, move_direction))
10493 if (!ACCESS_FROM(element, opposite_direction))
10494 return MP_NO_ACTION; /* field not accessible from this direction */
10496 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10497 return MP_NO_ACTION;
10499 if (IS_EM_GATE(element))
10501 if (!player->key[EM_GATE_NR(element)])
10502 return MP_NO_ACTION;
10504 else if (IS_EM_GATE_GRAY(element))
10506 if (!player->key[EM_GATE_GRAY_NR(element)])
10507 return MP_NO_ACTION;
10509 else if (IS_EM_GATE_GRAY_ACTIVE(element))
10511 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10512 return MP_NO_ACTION;
10514 else if (IS_SP_PORT(element))
10516 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10517 element == EL_SP_GRAVITY_PORT_RIGHT ||
10518 element == EL_SP_GRAVITY_PORT_UP ||
10519 element == EL_SP_GRAVITY_PORT_DOWN)
10520 game.gravity = !game.gravity;
10521 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10522 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10523 element == EL_SP_GRAVITY_ON_PORT_UP ||
10524 element == EL_SP_GRAVITY_ON_PORT_DOWN)
10525 game.gravity = TRUE;
10526 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10527 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10528 element == EL_SP_GRAVITY_OFF_PORT_UP ||
10529 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10530 game.gravity = FALSE;
10533 /* automatically move to the next field with double speed */
10534 player->programmed_action = move_direction;
10536 if (player->move_delay_reset_counter == 0)
10538 player->move_delay_reset_counter = 2; /* two double speed steps */
10540 DOUBLE_PLAYER_SPEED(player);
10543 PlayLevelSoundAction(x, y, ACTION_PASSING);
10545 else if (player_can_move_or_snap && IS_DIGGABLE(element))
10549 if (mode != DF_SNAP)
10551 GfxElement[x][y] = GFX_ELEMENT(element);
10552 player->is_digging = TRUE;
10555 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10557 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10558 player->index_bit, dig_side);
10560 if (mode == DF_SNAP)
10562 #if USE_NEW_SNAP_DELAY
10563 if (level.block_snap_field)
10564 setFieldForSnapping(x, y, element, move_direction);
10566 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10568 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10571 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10572 player->index_bit, dig_side);
10575 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
10579 if (is_player && mode != DF_SNAP)
10581 GfxElement[x][y] = element;
10582 player->is_collecting = TRUE;
10585 if (element == EL_SPEED_PILL)
10587 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10589 else if (element == EL_EXTRA_TIME && level.time > 0)
10591 TimeLeft += level.extra_time;
10592 DrawGameValue_Time(TimeLeft);
10594 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10596 player->shield_normal_time_left += level.shield_normal_time;
10597 if (element == EL_SHIELD_DEADLY)
10598 player->shield_deadly_time_left += level.shield_deadly_time;
10600 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10602 if (player->inventory_size < MAX_INVENTORY_SIZE)
10603 player->inventory_element[player->inventory_size++] = element;
10605 DrawGameValue_Dynamite(local_player->inventory_size);
10607 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10609 player->dynabomb_count++;
10610 player->dynabombs_left++;
10612 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10614 player->dynabomb_size++;
10616 else if (element == EL_DYNABOMB_INCREASE_POWER)
10618 player->dynabomb_xl = TRUE;
10620 else if (IS_KEY(element))
10622 player->key[KEY_NR(element)] = TRUE;
10624 DrawGameValue_Keys(player->key);
10626 redraw_mask |= REDRAW_DOOR_1;
10628 else if (IS_ENVELOPE(element))
10630 player->show_envelope = element;
10632 else if (element == EL_EMC_LENSES)
10634 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10636 RedrawAllInvisibleElementsForLenses();
10638 else if (element == EL_EMC_MAGNIFIER)
10640 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10642 RedrawAllInvisibleElementsForMagnifier();
10644 else if (IS_DROPPABLE(element) ||
10645 IS_THROWABLE(element)) /* can be collected and dropped */
10649 if (collect_count == 0)
10650 player->inventory_infinite_element = element;
10652 for (i = 0; i < collect_count; i++)
10653 if (player->inventory_size < MAX_INVENTORY_SIZE)
10654 player->inventory_element[player->inventory_size++] = element;
10656 DrawGameValue_Dynamite(local_player->inventory_size);
10658 else if (collect_count > 0)
10660 local_player->gems_still_needed -= collect_count;
10661 if (local_player->gems_still_needed < 0)
10662 local_player->gems_still_needed = 0;
10664 DrawGameValue_Emeralds(local_player->gems_still_needed);
10667 RaiseScoreElement(element);
10668 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10671 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10672 player->index_bit, dig_side);
10674 if (mode == DF_SNAP)
10676 #if USE_NEW_SNAP_DELAY
10677 if (level.block_snap_field)
10678 setFieldForSnapping(x, y, element, move_direction);
10680 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10682 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10685 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10686 player->index_bit, dig_side);
10689 else if (player_can_move_or_snap && IS_PUSHABLE(element))
10691 if (mode == DF_SNAP && element != EL_BD_ROCK)
10692 return MP_NO_ACTION;
10694 if (CAN_FALL(element) && dy)
10695 return MP_NO_ACTION;
10697 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10698 !(element == EL_SPRING && level.use_spring_bug))
10699 return MP_NO_ACTION;
10701 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10702 ((move_direction & MV_VERTICAL &&
10703 ((element_info[element].move_pattern & MV_LEFT &&
10704 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10705 (element_info[element].move_pattern & MV_RIGHT &&
10706 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10707 (move_direction & MV_HORIZONTAL &&
10708 ((element_info[element].move_pattern & MV_UP &&
10709 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10710 (element_info[element].move_pattern & MV_DOWN &&
10711 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10712 return MP_NO_ACTION;
10714 /* do not push elements already moving away faster than player */
10715 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10716 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10717 return MP_NO_ACTION;
10719 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10721 if (player->push_delay_value == -1 || !player_was_pushing)
10722 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10724 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10726 if (player->push_delay_value == -1)
10727 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10729 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10731 if (!player->is_pushing)
10732 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10735 player->is_pushing = TRUE;
10737 if (!(IN_LEV_FIELD(nextx, nexty) &&
10738 (IS_FREE(nextx, nexty) ||
10739 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10740 IS_SB_ELEMENT(element)))))
10741 return MP_NO_ACTION;
10743 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10744 return MP_NO_ACTION;
10746 if (player->push_delay == -1) /* new pushing; restart delay */
10747 player->push_delay = 0;
10749 if (player->push_delay < player->push_delay_value &&
10750 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10751 element != EL_SPRING && element != EL_BALLOON)
10753 /* make sure that there is no move delay before next try to push */
10754 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10755 player->move_delay = 0;
10757 return MP_NO_ACTION;
10760 if (IS_SB_ELEMENT(element))
10762 if (element == EL_SOKOBAN_FIELD_FULL)
10764 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10765 local_player->sokobanfields_still_needed++;
10768 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10770 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10771 local_player->sokobanfields_still_needed--;
10774 Feld[x][y] = EL_SOKOBAN_OBJECT;
10776 if (Back[x][y] == Back[nextx][nexty])
10777 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10778 else if (Back[x][y] != 0)
10779 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10782 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10785 if (local_player->sokobanfields_still_needed == 0 &&
10786 game.emulation == EMU_SOKOBAN)
10788 player->LevelSolved = player->GameOver = TRUE;
10789 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10793 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10795 InitMovingField(x, y, move_direction);
10796 GfxAction[x][y] = ACTION_PUSHING;
10798 if (mode == DF_SNAP)
10799 ContinueMoving(x, y);
10801 MovPos[x][y] = (dx != 0 ? dx : dy);
10803 Pushed[x][y] = TRUE;
10804 Pushed[nextx][nexty] = TRUE;
10806 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10807 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10809 player->push_delay_value = -1; /* get new value later */
10811 /* check for element change _after_ element has been pushed */
10812 if (game.use_change_when_pushing_bug)
10814 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10815 player->index_bit, dig_side);
10816 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10817 player->index_bit, dig_side);
10820 else if (IS_SWITCHABLE(element))
10822 if (PLAYER_SWITCHING(player, x, y))
10824 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10825 player->index_bit, dig_side);
10830 player->is_switching = TRUE;
10831 player->switch_x = x;
10832 player->switch_y = y;
10834 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10836 if (element == EL_ROBOT_WHEEL)
10838 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10842 DrawLevelField(x, y);
10844 else if (element == EL_SP_TERMINAL)
10849 SCAN_PLAYFIELD(xx, yy)
10851 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10854 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10856 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10857 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10860 else if (IS_BELT_SWITCH(element))
10862 ToggleBeltSwitch(x, y);
10864 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10865 element == EL_SWITCHGATE_SWITCH_DOWN)
10867 ToggleSwitchgateSwitch(x, y);
10869 else if (element == EL_LIGHT_SWITCH ||
10870 element == EL_LIGHT_SWITCH_ACTIVE)
10872 ToggleLightSwitch(x, y);
10874 else if (element == EL_TIMEGATE_SWITCH)
10876 ActivateTimegateSwitch(x, y);
10878 else if (element == EL_BALLOON_SWITCH_LEFT ||
10879 element == EL_BALLOON_SWITCH_RIGHT ||
10880 element == EL_BALLOON_SWITCH_UP ||
10881 element == EL_BALLOON_SWITCH_DOWN ||
10882 element == EL_BALLOON_SWITCH_NONE ||
10883 element == EL_BALLOON_SWITCH_ANY)
10885 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10886 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10887 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10888 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10889 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10892 else if (element == EL_LAMP)
10894 Feld[x][y] = EL_LAMP_ACTIVE;
10895 local_player->lights_still_needed--;
10897 ResetGfxAnimation(x, y);
10898 DrawLevelField(x, y);
10900 else if (element == EL_TIME_ORB_FULL)
10902 Feld[x][y] = EL_TIME_ORB_EMPTY;
10904 if (level.time > 0 || level.use_time_orb_bug)
10906 TimeLeft += level.time_orb_time;
10907 DrawGameValue_Time(TimeLeft);
10910 ResetGfxAnimation(x, y);
10911 DrawLevelField(x, y);
10913 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
10914 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10918 game.ball_state = !game.ball_state;
10921 SCAN_PLAYFIELD(xx, yy)
10923 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10926 int e = Feld[xx][yy];
10928 if (game.ball_state)
10930 if (e == EL_EMC_MAGIC_BALL)
10931 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
10932 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
10933 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
10937 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
10938 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
10939 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10940 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
10945 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10946 player->index_bit, dig_side);
10948 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10949 player->index_bit, dig_side);
10951 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10952 player->index_bit, dig_side);
10958 if (!PLAYER_SWITCHING(player, x, y))
10960 player->is_switching = TRUE;
10961 player->switch_x = x;
10962 player->switch_y = y;
10964 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10965 player->index_bit, dig_side);
10966 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10967 player->index_bit, dig_side);
10969 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
10970 player->index_bit, dig_side);
10971 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10972 player->index_bit, dig_side);
10975 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10976 player->index_bit, dig_side);
10977 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10978 player->index_bit, dig_side);
10980 return MP_NO_ACTION;
10983 player->push_delay = -1;
10985 if (is_player) /* function can also be called by EL_PENGUIN */
10987 if (Feld[x][y] != element) /* really digged/collected something */
10988 player->is_collecting = !player->is_digging;
10994 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10996 int jx = player->jx, jy = player->jy;
10997 int x = jx + dx, y = jy + dy;
10998 int snap_direction = (dx == -1 ? MV_LEFT :
10999 dx == +1 ? MV_RIGHT :
11001 dy == +1 ? MV_DOWN : MV_NONE);
11003 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11006 if (!player->active || !IN_LEV_FIELD(x, y))
11014 if (player->MovPos == 0)
11015 player->is_pushing = FALSE;
11017 player->is_snapping = FALSE;
11019 if (player->MovPos == 0)
11021 player->is_moving = FALSE;
11022 player->is_digging = FALSE;
11023 player->is_collecting = FALSE;
11029 if (player->is_snapping)
11032 player->MovDir = snap_direction;
11034 if (player->MovPos == 0)
11036 player->is_moving = FALSE;
11037 player->is_digging = FALSE;
11038 player->is_collecting = FALSE;
11041 player->is_dropping = FALSE;
11043 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11046 player->is_snapping = TRUE;
11048 if (player->MovPos == 0)
11050 player->is_moving = FALSE;
11051 player->is_digging = FALSE;
11052 player->is_collecting = FALSE;
11055 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11056 DrawLevelField(player->last_jx, player->last_jy);
11058 DrawLevelField(x, y);
11063 boolean DropElement(struct PlayerInfo *player)
11065 int old_element, new_element;
11066 int dropx = player->jx, dropy = player->jy;
11067 int drop_direction = player->MovDir;
11068 int drop_side = drop_direction;
11069 int drop_element = (player->inventory_size > 0 ?
11070 player->inventory_element[player->inventory_size - 1] :
11071 player->inventory_infinite_element != EL_UNDEFINED ?
11072 player->inventory_infinite_element :
11073 player->dynabombs_left > 0 ?
11074 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11077 /* do not drop an element on top of another element; when holding drop key
11078 pressed without moving, dropped element must move away before the next
11079 element can be dropped (this is especially important if the next element
11080 is dynamite, which can be placed on background for historical reasons) */
11081 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11084 if (IS_THROWABLE(drop_element))
11086 dropx += GET_DX_FROM_DIR(drop_direction);
11087 dropy += GET_DY_FROM_DIR(drop_direction);
11089 if (!IN_LEV_FIELD(dropx, dropy))
11093 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11094 new_element = drop_element; /* default: no change when dropping */
11096 /* check if player is active, not moving and ready to drop */
11097 if (!player->active || player->MovPos || player->drop_delay > 0)
11100 /* check if player has anything that can be dropped */
11101 if (new_element == EL_UNDEFINED)
11104 /* check if anything can be dropped at the current position */
11105 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11108 /* collected custom elements can only be dropped on empty fields */
11109 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11112 if (old_element != EL_EMPTY)
11113 Back[dropx][dropy] = old_element; /* store old element on this field */
11115 ResetGfxAnimation(dropx, dropy);
11116 ResetRandomAnimationValue(dropx, dropy);
11118 if (player->inventory_size > 0 ||
11119 player->inventory_infinite_element != EL_UNDEFINED)
11121 if (player->inventory_size > 0)
11123 player->inventory_size--;
11125 DrawGameValue_Dynamite(local_player->inventory_size);
11127 if (new_element == EL_DYNAMITE)
11128 new_element = EL_DYNAMITE_ACTIVE;
11129 else if (new_element == EL_SP_DISK_RED)
11130 new_element = EL_SP_DISK_RED_ACTIVE;
11133 Feld[dropx][dropy] = new_element;
11135 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11136 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11137 el2img(Feld[dropx][dropy]), 0);
11139 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11141 /* needed if previous element just changed to "empty" in the last frame */
11142 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11144 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11145 player->index_bit, drop_side);
11146 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11148 player->index_bit, drop_side);
11150 TestIfElementTouchesCustomElement(dropx, dropy);
11152 else /* player is dropping a dyna bomb */
11154 player->dynabombs_left--;
11156 Feld[dropx][dropy] = new_element;
11158 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11159 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11160 el2img(Feld[dropx][dropy]), 0);
11162 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11165 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11166 InitField_WithBug1(dropx, dropy, FALSE);
11168 new_element = Feld[dropx][dropy]; /* element might have changed */
11170 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11171 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11173 int move_direction, nextx, nexty;
11175 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11176 MovDir[dropx][dropy] = drop_direction;
11178 move_direction = MovDir[dropx][dropy];
11179 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11180 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11182 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11183 CheckCollision[dropx][dropy] = 2;
11186 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11187 player->is_dropping = TRUE;
11189 player->drop_x = dropx;
11190 player->drop_y = dropy;
11195 /* ------------------------------------------------------------------------- */
11196 /* game sound playing functions */
11197 /* ------------------------------------------------------------------------- */
11199 static int *loop_sound_frame = NULL;
11200 static int *loop_sound_volume = NULL;
11202 void InitPlayLevelSound()
11204 int num_sounds = getSoundListSize();
11206 checked_free(loop_sound_frame);
11207 checked_free(loop_sound_volume);
11209 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11210 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11213 static void PlayLevelSound(int x, int y, int nr)
11215 int sx = SCREENX(x), sy = SCREENY(y);
11216 int volume, stereo_position;
11217 int max_distance = 8;
11218 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11220 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11221 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11224 if (!IN_LEV_FIELD(x, y) ||
11225 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11226 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11229 volume = SOUND_MAX_VOLUME;
11231 if (!IN_SCR_FIELD(sx, sy))
11233 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11234 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11236 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11239 stereo_position = (SOUND_MAX_LEFT +
11240 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11241 (SCR_FIELDX + 2 * max_distance));
11243 if (IS_LOOP_SOUND(nr))
11245 /* This assures that quieter loop sounds do not overwrite louder ones,
11246 while restarting sound volume comparison with each new game frame. */
11248 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11251 loop_sound_volume[nr] = volume;
11252 loop_sound_frame[nr] = FrameCounter;
11255 PlaySoundExt(nr, volume, stereo_position, type);
11258 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11260 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11261 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11262 y < LEVELY(BY1) ? LEVELY(BY1) :
11263 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11267 static void PlayLevelSoundAction(int x, int y, int action)
11269 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11272 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11274 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11276 if (sound_effect != SND_UNDEFINED)
11277 PlayLevelSound(x, y, sound_effect);
11280 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11283 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11285 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11286 PlayLevelSound(x, y, sound_effect);
11289 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11291 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11293 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11294 PlayLevelSound(x, y, sound_effect);
11297 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11299 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11301 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11302 StopSound(sound_effect);
11305 static void PlayLevelMusic()
11307 if (levelset.music[level_nr] != MUS_UNDEFINED)
11308 PlayMusic(levelset.music[level_nr]); /* from config file */
11310 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11313 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11315 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11320 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11324 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11328 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11332 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11336 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11340 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11344 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11347 case SAMPLE_android_clone:
11348 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11351 case SAMPLE_android_move:
11352 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11355 case SAMPLE_spring:
11356 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11360 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
11364 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11367 case SAMPLE_eater_eat:
11368 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11372 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11375 case SAMPLE_collect:
11376 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11379 case SAMPLE_diamond:
11380 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11383 case SAMPLE_squash:
11384 /* !!! CHECK THIS !!! */
11386 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11388 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11392 case SAMPLE_wonderfall:
11393 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11397 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11401 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11405 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11409 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11413 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11417 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11420 case SAMPLE_wonder:
11421 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11425 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11428 case SAMPLE_exit_open:
11429 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11432 case SAMPLE_exit_leave:
11433 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11436 case SAMPLE_dynamite:
11437 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11441 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11445 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11449 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11453 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11457 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11461 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11465 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11470 void RaiseScore(int value)
11472 local_player->score += value;
11474 DrawGameValue_Score(local_player->score);
11477 void RaiseScoreElement(int element)
11482 case EL_BD_DIAMOND:
11483 case EL_EMERALD_YELLOW:
11484 case EL_EMERALD_RED:
11485 case EL_EMERALD_PURPLE:
11486 case EL_SP_INFOTRON:
11487 RaiseScore(level.score[SC_EMERALD]);
11490 RaiseScore(level.score[SC_DIAMOND]);
11493 RaiseScore(level.score[SC_CRYSTAL]);
11496 RaiseScore(level.score[SC_PEARL]);
11499 case EL_BD_BUTTERFLY:
11500 case EL_SP_ELECTRON:
11501 RaiseScore(level.score[SC_BUG]);
11504 case EL_BD_FIREFLY:
11505 case EL_SP_SNIKSNAK:
11506 RaiseScore(level.score[SC_SPACESHIP]);
11509 case EL_DARK_YAMYAM:
11510 RaiseScore(level.score[SC_YAMYAM]);
11513 RaiseScore(level.score[SC_ROBOT]);
11516 RaiseScore(level.score[SC_PACMAN]);
11519 RaiseScore(level.score[SC_NUT]);
11522 case EL_SP_DISK_RED:
11523 case EL_DYNABOMB_INCREASE_NUMBER:
11524 case EL_DYNABOMB_INCREASE_SIZE:
11525 case EL_DYNABOMB_INCREASE_POWER:
11526 RaiseScore(level.score[SC_DYNAMITE]);
11528 case EL_SHIELD_NORMAL:
11529 case EL_SHIELD_DEADLY:
11530 RaiseScore(level.score[SC_SHIELD]);
11532 case EL_EXTRA_TIME:
11533 RaiseScore(level.extra_time_score);
11547 RaiseScore(level.score[SC_KEY]);
11550 RaiseScore(element_info[element].collect_score);
11555 void RequestQuitGame(boolean ask_if_really_quit)
11557 if (AllPlayersGone ||
11558 !ask_if_really_quit ||
11559 level_editor_test_game ||
11560 Request("Do you really want to quit the game ?",
11561 REQ_ASK | REQ_STAY_CLOSED))
11563 #if defined(NETWORK_AVALIABLE)
11564 if (options.network)
11565 SendToServer_StopPlaying();
11569 game_status = GAME_MODE_MAIN;
11575 if (tape.playing && tape.deactivate_display)
11576 TapeDeactivateDisplayOff(TRUE);
11578 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11580 if (tape.playing && tape.deactivate_display)
11581 TapeDeactivateDisplayOn();
11586 /* ---------- new game button stuff ---------------------------------------- */
11588 /* graphic position values for game buttons */
11589 #define GAME_BUTTON_XSIZE 30
11590 #define GAME_BUTTON_YSIZE 30
11591 #define GAME_BUTTON_XPOS 5
11592 #define GAME_BUTTON_YPOS 215
11593 #define SOUND_BUTTON_XPOS 5
11594 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11596 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11597 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11598 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11599 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11600 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11601 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11608 } gamebutton_info[NUM_GAME_BUTTONS] =
11611 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11616 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11617 GAME_CTRL_ID_PAUSE,
11621 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11626 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11627 SOUND_CTRL_ID_MUSIC,
11628 "background music on/off"
11631 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11632 SOUND_CTRL_ID_LOOPS,
11633 "sound loops on/off"
11636 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11637 SOUND_CTRL_ID_SIMPLE,
11638 "normal sounds on/off"
11642 void CreateGameButtons()
11646 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11648 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11649 struct GadgetInfo *gi;
11652 unsigned long event_mask;
11653 int gd_xoffset, gd_yoffset;
11654 int gd_x1, gd_x2, gd_y1, gd_y2;
11657 gd_xoffset = gamebutton_info[i].x;
11658 gd_yoffset = gamebutton_info[i].y;
11659 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11660 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11662 if (id == GAME_CTRL_ID_STOP ||
11663 id == GAME_CTRL_ID_PAUSE ||
11664 id == GAME_CTRL_ID_PLAY)
11666 button_type = GD_TYPE_NORMAL_BUTTON;
11668 event_mask = GD_EVENT_RELEASED;
11669 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11670 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11674 button_type = GD_TYPE_CHECK_BUTTON;
11676 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11677 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11678 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11679 event_mask = GD_EVENT_PRESSED;
11680 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11681 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11684 gi = CreateGadget(GDI_CUSTOM_ID, id,
11685 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11686 GDI_X, DX + gd_xoffset,
11687 GDI_Y, DY + gd_yoffset,
11688 GDI_WIDTH, GAME_BUTTON_XSIZE,
11689 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11690 GDI_TYPE, button_type,
11691 GDI_STATE, GD_BUTTON_UNPRESSED,
11692 GDI_CHECKED, checked,
11693 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11694 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11695 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11696 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11697 GDI_EVENT_MASK, event_mask,
11698 GDI_CALLBACK_ACTION, HandleGameButtons,
11702 Error(ERR_EXIT, "cannot create gadget");
11704 game_gadget[id] = gi;
11708 void FreeGameButtons()
11712 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11713 FreeGadget(game_gadget[i]);
11716 static void MapGameButtons()
11720 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11721 MapGadget(game_gadget[i]);
11724 void UnmapGameButtons()
11728 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11729 UnmapGadget(game_gadget[i]);
11732 static void HandleGameButtons(struct GadgetInfo *gi)
11734 int id = gi->custom_id;
11736 if (game_status != GAME_MODE_PLAYING)
11741 case GAME_CTRL_ID_STOP:
11742 RequestQuitGame(TRUE);
11745 case GAME_CTRL_ID_PAUSE:
11746 if (options.network)
11748 #if defined(NETWORK_AVALIABLE)
11750 SendToServer_ContinuePlaying();
11752 SendToServer_PausePlaying();
11756 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11759 case GAME_CTRL_ID_PLAY:
11762 #if defined(NETWORK_AVALIABLE)
11763 if (options.network)
11764 SendToServer_ContinuePlaying();
11768 tape.pausing = FALSE;
11769 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11774 case SOUND_CTRL_ID_MUSIC:
11775 if (setup.sound_music)
11777 setup.sound_music = FALSE;
11780 else if (audio.music_available)
11782 setup.sound = setup.sound_music = TRUE;
11784 SetAudioMode(setup.sound);
11790 case SOUND_CTRL_ID_LOOPS:
11791 if (setup.sound_loops)
11792 setup.sound_loops = FALSE;
11793 else if (audio.loops_available)
11795 setup.sound = setup.sound_loops = TRUE;
11796 SetAudioMode(setup.sound);
11800 case SOUND_CTRL_ID_SIMPLE:
11801 if (setup.sound_simple)
11802 setup.sound_simple = FALSE;
11803 else if (audio.sound_available)
11805 setup.sound = setup.sound_simple = TRUE;
11806 SetAudioMode(setup.sound);