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)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
45 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 /* for MovePlayer() */
53 #define MP_NO_ACTION 0
56 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
58 /* for ScrollPlayer() */
60 #define SCROLL_GO_ON 1
62 /* for Bang()/Explode() */
63 #define EX_PHASE_START 0
64 #define EX_TYPE_NONE 0
65 #define EX_TYPE_NORMAL (1 << 0)
66 #define EX_TYPE_CENTER (1 << 1)
67 #define EX_TYPE_BORDER (1 << 2)
68 #define EX_TYPE_CROSS (1 << 3)
69 #define EX_TYPE_DYNA (1 << 4)
70 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
72 /* special positions in the game control window (relative to control window) */
75 #define XX_EMERALDS 29
76 #define YY_EMERALDS 54
77 #define XX_DYNAMITE 29
78 #define YY_DYNAMITE 89
87 /* special positions in the game control window (relative to main window) */
88 #define DX_LEVEL (DX + XX_LEVEL)
89 #define DY_LEVEL (DY + YY_LEVEL)
90 #define DX_EMERALDS (DX + XX_EMERALDS)
91 #define DY_EMERALDS (DY + YY_EMERALDS)
92 #define DX_DYNAMITE (DX + XX_DYNAMITE)
93 #define DY_DYNAMITE (DY + YY_DYNAMITE)
94 #define DX_KEYS (DX + XX_KEYS)
95 #define DY_KEYS (DY + YY_KEYS)
96 #define DX_SCORE (DX + XX_SCORE)
97 #define DY_SCORE (DY + YY_SCORE)
98 #define DX_TIME1 (DX + XX_TIME1)
99 #define DX_TIME2 (DX + XX_TIME2)
100 #define DY_TIME (DY + YY_TIME)
102 /* values for delayed check of falling and moving elements and for collision */
103 #define CHECK_DELAY_MOVING 3
104 #define CHECK_DELAY_FALLING 3
105 #define CHECK_DELAY_COLLISION 2
107 /* values for initial player move delay (initial delay counter value) */
108 #define INITIAL_MOVE_DELAY_OFF -1
109 #define INITIAL_MOVE_DELAY_ON 0
111 /* values for player movement speed (which is in fact a delay value) */
112 #define MOVE_DELAY_MIN_SPEED 32
113 #define MOVE_DELAY_NORMAL_SPEED 8
114 #define MOVE_DELAY_HIGH_SPEED 4
115 #define MOVE_DELAY_MAX_SPEED 1
118 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
119 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
121 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
122 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
124 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
125 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
127 /* values for other actions */
128 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
129 #define MOVE_STEPSIZE_MIN (1)
130 #define MOVE_STEPSIZE_MAX (TILEX)
132 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
133 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
135 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
137 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
138 RND(element_info[e].push_delay_random))
139 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
140 RND(element_info[e].drop_delay_random))
141 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
142 RND(element_info[e].move_delay_random))
143 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
144 (element_info[e].move_delay_random))
145 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
146 RND(element_info[e].ce_value_random_initial))
147 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
148 RND((c)->delay_random * (c)->delay_frames))
149 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
150 RND((c)->delay_random))
153 #define GET_VALID_RUNTIME_ELEMENT(e) \
154 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
156 #define GET_VALID_FILE_ELEMENT(e) \
157 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
160 #define GET_TARGET_ELEMENT(e, ch) \
161 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
162 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
163 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
165 #define CAN_GROW_INTO(e) \
166 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
168 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
169 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
172 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
173 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
174 (CAN_MOVE_INTO_ACID(e) && \
175 Feld[x][y] == EL_ACID) || \
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
184 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
185 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
187 (CAN_MOVE_INTO_ACID(e) && \
188 Feld[x][y] == EL_ACID) || \
189 (DONT_COLLIDE_WITH(e) && \
191 !PLAYER_ENEMY_PROTECTED(x, y))))
193 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
196 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
199 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
202 #define ANDROID_CAN_CLONE_FIELD(x, y) \
203 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
204 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
206 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
212 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
215 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
223 IS_FOOD_PENGUIN(Feld[x][y])))
224 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
230 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
234 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
235 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
238 #define GROUP_NR(e) ((e) - EL_GROUP_START)
239 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
240 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
242 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
243 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
246 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
248 #define CE_ENTER_FIELD_COND(e, x, y) \
249 (!IS_PLAYER(x, y) && \
250 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
252 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
255 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
256 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
258 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
259 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
260 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
261 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
263 /* game button identifiers */
264 #define GAME_CTRL_ID_STOP 0
265 #define GAME_CTRL_ID_PAUSE 1
266 #define GAME_CTRL_ID_PLAY 2
267 #define SOUND_CTRL_ID_MUSIC 3
268 #define SOUND_CTRL_ID_LOOPS 4
269 #define SOUND_CTRL_ID_SIMPLE 5
271 #define NUM_GAME_BUTTONS 6
274 /* forward declaration for internal use */
276 static void CreateField(int, int, int);
278 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
279 static void AdvanceFrameAndPlayerCounters(int);
281 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
282 static boolean MovePlayer(struct PlayerInfo *, int, int);
283 static void ScrollPlayer(struct PlayerInfo *, int);
284 static void ScrollScreen(struct PlayerInfo *, int);
286 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
288 static void InitBeltMovement(void);
289 static void CloseAllOpenTimegates(void);
290 static void CheckGravityMovement(struct PlayerInfo *);
291 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
292 static void KillPlayerUnlessEnemyProtected(int, int);
293 static void KillPlayerUnlessExplosionProtected(int, int);
295 static void TestIfPlayerTouchesCustomElement(int, int);
296 static void TestIfElementTouchesCustomElement(int, int);
297 static void TestIfElementHitsCustomElement(int, int, int);
299 static void TestIfElementSmashesCustomElement(int, int, int);
302 static void HandleElementChange(int, int, int);
303 static void ExecuteCustomElementAction(int, int, int, int);
304 static boolean ChangeElement(int, int, int, int);
306 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
307 #define CheckTriggeredElementChange(x, y, e, ev) \
308 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
309 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
310 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
311 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
312 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
313 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
314 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
316 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
317 #define CheckElementChange(x, y, e, te, ev) \
318 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
319 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
321 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
322 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
324 static void PlayLevelSound(int, int, int);
325 static void PlayLevelSoundNearest(int, int, int);
326 static void PlayLevelSoundAction(int, int, int);
327 static void PlayLevelSoundElementAction(int, int, int, int);
328 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
329 static void PlayLevelSoundActionIfLoop(int, int, int);
330 static void StopLevelSoundActionIfLoop(int, int, int);
331 static void PlayLevelMusic();
333 static void MapGameButtons();
334 static void HandleGameButtons(struct GadgetInfo *);
336 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
339 /* ------------------------------------------------------------------------- */
340 /* definition of elements that automatically change to other elements after */
341 /* a specified time, eventually calling a function when changing */
342 /* ------------------------------------------------------------------------- */
344 /* forward declaration for changer functions */
345 static void InitBuggyBase(int, int);
346 static void WarnBuggyBase(int, int);
348 static void InitTrap(int, int);
349 static void ActivateTrap(int, int);
350 static void ChangeActiveTrap(int, int);
352 static void InitRobotWheel(int, int);
353 static void RunRobotWheel(int, int);
354 static void StopRobotWheel(int, int);
356 static void InitTimegateWheel(int, int);
357 static void RunTimegateWheel(int, int);
359 static void InitMagicBallDelay(int, int);
360 static void ActivateMagicBall(int, int);
362 static void InitDiagonalMovingElement(int, int);
364 struct ChangingElementInfo
369 void (*pre_change_function)(int x, int y);
370 void (*change_function)(int x, int y);
371 void (*post_change_function)(int x, int y);
374 static struct ChangingElementInfo change_delay_list[] =
425 EL_SWITCHGATE_OPENING,
433 EL_SWITCHGATE_CLOSING,
434 EL_SWITCHGATE_CLOSED,
466 EL_ACID_SPLASH_RIGHT,
475 EL_SP_BUGGY_BASE_ACTIVATING,
482 EL_SP_BUGGY_BASE_ACTIVATING,
483 EL_SP_BUGGY_BASE_ACTIVE,
490 EL_SP_BUGGY_BASE_ACTIVE,
514 EL_ROBOT_WHEEL_ACTIVE,
522 EL_TIMEGATE_SWITCH_ACTIVE,
530 EL_EMC_MAGIC_BALL_ACTIVE,
531 EL_EMC_MAGIC_BALL_ACTIVE,
538 EL_EMC_SPRING_BUMPER_ACTIVE,
539 EL_EMC_SPRING_BUMPER,
546 EL_DIAGONAL_SHRINKING,
559 InitDiagonalMovingElement
575 int push_delay_fixed, push_delay_random;
580 { EL_BALLOON, 0, 0 },
582 { EL_SOKOBAN_OBJECT, 2, 0 },
583 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
584 { EL_SATELLITE, 2, 0 },
585 { EL_SP_DISK_YELLOW, 2, 0 },
587 { EL_UNDEFINED, 0, 0 },
595 move_stepsize_list[] =
597 { EL_AMOEBA_DROP, 2 },
598 { EL_AMOEBA_DROPPING, 2 },
599 { EL_QUICKSAND_FILLING, 1 },
600 { EL_QUICKSAND_EMPTYING, 1 },
601 { EL_MAGIC_WALL_FILLING, 2 },
602 { EL_BD_MAGIC_WALL_FILLING, 2 },
603 { EL_MAGIC_WALL_EMPTYING, 2 },
604 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
614 collect_count_list[] =
617 { EL_BD_DIAMOND, 1 },
618 { EL_EMERALD_YELLOW, 1 },
619 { EL_EMERALD_RED, 1 },
620 { EL_EMERALD_PURPLE, 1 },
622 { EL_SP_INFOTRON, 1 },
634 access_direction_list[] =
636 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
637 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
638 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
639 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
640 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
641 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
642 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
643 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
644 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
645 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
646 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
648 { EL_SP_PORT_LEFT, MV_RIGHT },
649 { EL_SP_PORT_RIGHT, MV_LEFT },
650 { EL_SP_PORT_UP, MV_DOWN },
651 { EL_SP_PORT_DOWN, MV_UP },
652 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
653 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
654 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
655 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
656 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
657 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
658 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
659 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
660 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
661 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
662 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
663 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
664 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
665 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
666 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
668 { EL_UNDEFINED, MV_NONE }
671 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
673 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
674 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
675 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
676 IS_JUST_CHANGING(x, y))
678 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
680 /* static variables for playfield scan mode (scanning forward or backward) */
681 static int playfield_scan_start_x = 0;
682 static int playfield_scan_start_y = 0;
683 static int playfield_scan_delta_x = 1;
684 static int playfield_scan_delta_y = 1;
686 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
687 (y) >= 0 && (y) <= lev_fieldy - 1; \
688 (y) += playfield_scan_delta_y) \
689 for ((x) = playfield_scan_start_x; \
690 (x) >= 0 && (x) <= lev_fieldx - 1; \
691 (x) += playfield_scan_delta_x) \
693 static void InitPlayfieldScanModeVars()
695 if (game.use_reverse_scan_direction)
697 playfield_scan_start_x = lev_fieldx - 1;
698 playfield_scan_start_y = lev_fieldy - 1;
700 playfield_scan_delta_x = -1;
701 playfield_scan_delta_y = -1;
705 playfield_scan_start_x = 0;
706 playfield_scan_start_y = 0;
708 playfield_scan_delta_x = 1;
709 playfield_scan_delta_y = 1;
713 static void InitPlayfieldScanMode(int mode)
715 game.use_reverse_scan_direction =
716 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
718 InitPlayfieldScanModeVars();
721 static int get_move_delay_from_stepsize(int move_stepsize)
724 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
726 /* make sure that stepsize value is always a power of 2 */
727 move_stepsize = (1 << log_2(move_stepsize));
729 return TILEX / move_stepsize;
732 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
735 int move_delay = get_move_delay_from_stepsize(move_stepsize);
736 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
738 /* do no immediately change move delay -- the player might just be moving */
739 player->move_delay_value_next = move_delay;
741 /* information if player can move must be set separately */
742 player->cannot_move = cannot_move;
746 player->move_delay = game.initial_move_delay;
747 player->move_delay_value = game.initial_move_delay_value;
749 player->move_delay_value_next = -1;
751 player->move_delay_reset_counter = 0;
755 void GetPlayerConfig()
757 if (!audio.sound_available)
758 setup.sound_simple = FALSE;
760 if (!audio.loops_available)
761 setup.sound_loops = FALSE;
763 if (!audio.music_available)
764 setup.sound_music = FALSE;
766 if (!video.fullscreen_available)
767 setup.fullscreen = FALSE;
769 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
771 SetAudioMode(setup.sound);
775 static int getBeltNrFromBeltElement(int element)
777 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
778 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
779 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
782 static int getBeltNrFromBeltActiveElement(int element)
784 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
785 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
786 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
789 static int getBeltNrFromBeltSwitchElement(int element)
791 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
792 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
793 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
796 static int getBeltDirNrFromBeltSwitchElement(int element)
798 static int belt_base_element[4] =
800 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
801 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
802 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
803 EL_CONVEYOR_BELT_4_SWITCH_LEFT
806 int belt_nr = getBeltNrFromBeltSwitchElement(element);
807 int belt_dir_nr = element - belt_base_element[belt_nr];
809 return (belt_dir_nr % 3);
812 static int getBeltDirFromBeltSwitchElement(int element)
814 static int belt_move_dir[3] =
821 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
823 return belt_move_dir[belt_dir_nr];
826 static void InitPlayerField(int x, int y, int element, boolean init_game)
828 if (element == EL_SP_MURPHY)
832 if (stored_player[0].present)
834 Feld[x][y] = EL_SP_MURPHY_CLONE;
840 stored_player[0].use_murphy = TRUE;
842 if (!level.use_artwork_element[0])
843 stored_player[0].artwork_element = EL_SP_MURPHY;
846 Feld[x][y] = EL_PLAYER_1;
852 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
853 int jx = player->jx, jy = player->jy;
855 player->present = TRUE;
857 player->block_last_field = (element == EL_SP_MURPHY ?
858 level.sp_block_last_field :
859 level.block_last_field);
861 /* ---------- initialize player's last field block delay --------------- */
863 /* always start with reliable default value (no adjustment needed) */
864 player->block_delay_adjustment = 0;
866 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
867 if (player->block_last_field && element == EL_SP_MURPHY)
868 player->block_delay_adjustment = 1;
870 /* special case 2: in game engines before 3.1.1, blocking was different */
871 if (game.use_block_last_field_bug)
872 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
874 if (!options.network || player->connected)
876 player->active = TRUE;
878 /* remove potentially duplicate players */
879 if (StorePlayer[jx][jy] == Feld[x][y])
880 StorePlayer[jx][jy] = 0;
882 StorePlayer[x][y] = Feld[x][y];
886 printf("Player %d activated.\n", player->element_nr);
887 printf("[Local player is %d and currently %s.]\n",
888 local_player->element_nr,
889 local_player->active ? "active" : "not active");
893 Feld[x][y] = EL_EMPTY;
895 player->jx = player->last_jx = x;
896 player->jy = player->last_jy = y;
900 static void InitField(int x, int y, boolean init_game)
902 int element = Feld[x][y];
911 InitPlayerField(x, y, element, init_game);
914 case EL_SOKOBAN_FIELD_PLAYER:
915 element = Feld[x][y] = EL_PLAYER_1;
916 InitField(x, y, init_game);
918 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
919 InitField(x, y, init_game);
922 case EL_SOKOBAN_FIELD_EMPTY:
923 local_player->sokobanfields_still_needed++;
927 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
928 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
929 else if (x > 0 && Feld[x-1][y] == EL_ACID)
930 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
931 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
932 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
933 else if (y > 0 && Feld[x][y-1] == EL_ACID)
934 Feld[x][y] = EL_ACID_POOL_BOTTOM;
935 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
936 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
944 case EL_SPACESHIP_RIGHT:
945 case EL_SPACESHIP_UP:
946 case EL_SPACESHIP_LEFT:
947 case EL_SPACESHIP_DOWN:
949 case EL_BD_BUTTERFLY_RIGHT:
950 case EL_BD_BUTTERFLY_UP:
951 case EL_BD_BUTTERFLY_LEFT:
952 case EL_BD_BUTTERFLY_DOWN:
953 case EL_BD_BUTTERFLY:
954 case EL_BD_FIREFLY_RIGHT:
955 case EL_BD_FIREFLY_UP:
956 case EL_BD_FIREFLY_LEFT:
957 case EL_BD_FIREFLY_DOWN:
959 case EL_PACMAN_RIGHT:
983 if (y == lev_fieldy - 1)
985 Feld[x][y] = EL_AMOEBA_GROWING;
986 Store[x][y] = EL_AMOEBA_WET;
990 case EL_DYNAMITE_ACTIVE:
991 case EL_SP_DISK_RED_ACTIVE:
992 case EL_DYNABOMB_PLAYER_1_ACTIVE:
993 case EL_DYNABOMB_PLAYER_2_ACTIVE:
994 case EL_DYNABOMB_PLAYER_3_ACTIVE:
995 case EL_DYNABOMB_PLAYER_4_ACTIVE:
999 case EL_EM_DYNAMITE_ACTIVE:
1000 MovDelay[x][y] = 32;
1004 local_player->lights_still_needed++;
1008 local_player->friends_still_needed++;
1013 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1016 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1017 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1018 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1019 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1020 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1021 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1022 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1023 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1024 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1025 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1026 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1027 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1030 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1031 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1032 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1034 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1036 game.belt_dir[belt_nr] = belt_dir;
1037 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1039 else /* more than one switch -- set it like the first switch */
1041 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1046 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1048 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1051 case EL_LIGHT_SWITCH_ACTIVE:
1053 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1056 case EL_EMC_MAGIC_BALL:
1057 if (game.ball_state)
1058 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1061 case EL_EMC_MAGIC_BALL_SWITCH:
1062 if (game.ball_state)
1063 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1068 if (IS_CUSTOM_ELEMENT(element))
1070 if (CAN_MOVE(element))
1073 #if USE_NEW_CUSTOM_VALUE
1074 if (!element_info[element].use_last_ce_value || init_game)
1075 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1079 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1082 else if (IS_GROUP_ELEMENT(element))
1084 struct ElementGroupInfo *group = element_info[element].group;
1085 int last_anim_random_frame = gfx.anim_random_frame;
1088 if (group->choice_mode == ANIM_RANDOM)
1089 gfx.anim_random_frame = RND(group->num_elements_resolved);
1091 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1092 group->choice_mode, 0,
1095 if (group->choice_mode == ANIM_RANDOM)
1096 gfx.anim_random_frame = last_anim_random_frame;
1098 group->choice_pos++;
1100 Feld[x][y] = group->element_resolved[element_pos];
1102 InitField(x, y, init_game);
1109 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1114 #if USE_NEW_CUSTOM_VALUE
1117 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1119 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1127 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1129 InitField(x, y, init_game);
1131 /* not needed to call InitMovDir() -- already done by InitField()! */
1132 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1133 CAN_MOVE(Feld[x][y]))
1137 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1139 int old_element = Feld[x][y];
1141 InitField(x, y, init_game);
1143 /* not needed to call InitMovDir() -- already done by InitField()! */
1144 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1145 CAN_MOVE(old_element) &&
1146 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1149 /* this case is in fact a combination of not less than three bugs:
1150 first, it calls InitMovDir() for elements that can move, although this is
1151 already done by InitField(); then, it checks the element that was at this
1152 field _before_ the call to InitField() (which can change it); lastly, it
1153 was not called for "mole with direction" elements, which were treated as
1154 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1158 inline void DrawGameValue_Emeralds(int value)
1160 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1163 inline void DrawGameValue_Dynamite(int value)
1165 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1168 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1172 /* currently only 4 of 8 possible keys are displayed */
1173 for (i = 0; i < STD_NUM_KEYS; i++)
1176 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1177 el2edimg(EL_KEY_1 + i));
1179 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1180 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1181 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1185 inline void DrawGameValue_Score(int value)
1187 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1190 inline void DrawGameValue_Time(int value)
1193 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1195 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1198 inline void DrawGameValue_Level(int value)
1201 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1204 /* misuse area for displaying emeralds to draw bigger level number */
1205 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1206 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1208 /* now copy it to the area for displaying level number */
1209 BlitBitmap(drawto, drawto,
1210 DX_EMERALDS, DY_EMERALDS + 1,
1211 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1212 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1213 DX_LEVEL - 1, DY_LEVEL + 1);
1215 /* restore the area for displaying emeralds */
1216 DrawGameValue_Emeralds(local_player->gems_still_needed);
1218 /* yes, this is all really ugly :-) */
1222 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1225 int key[MAX_NUM_KEYS];
1228 for (i = 0; i < MAX_NUM_KEYS; i++)
1229 key[i] = key_bits & (1 << i);
1231 DrawGameValue_Level(level_nr);
1233 DrawGameValue_Emeralds(emeralds);
1234 DrawGameValue_Dynamite(dynamite);
1235 DrawGameValue_Score(score);
1236 DrawGameValue_Time(time);
1238 DrawGameValue_Keys(key);
1241 void DrawGameDoorValues()
1245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1247 DrawGameDoorValues_EM();
1252 DrawGameValue_Level(level_nr);
1254 DrawGameValue_Emeralds(local_player->gems_still_needed);
1255 DrawGameValue_Dynamite(local_player->inventory_size);
1256 DrawGameValue_Score(local_player->score);
1257 DrawGameValue_Time(TimeLeft);
1259 for (i = 0; i < MAX_PLAYERS; i++)
1260 DrawGameValue_Keys(stored_player[i].key);
1264 static void resolve_group_element(int group_element, int recursion_depth)
1266 static int group_nr;
1267 static struct ElementGroupInfo *group;
1268 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1271 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1273 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1274 group_element - EL_GROUP_START + 1);
1276 /* replace element which caused too deep recursion by question mark */
1277 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1282 if (recursion_depth == 0) /* initialization */
1284 group = element_info[group_element].group;
1285 group_nr = group_element - EL_GROUP_START;
1287 group->num_elements_resolved = 0;
1288 group->choice_pos = 0;
1291 for (i = 0; i < actual_group->num_elements; i++)
1293 int element = actual_group->element[i];
1295 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1298 if (IS_GROUP_ELEMENT(element))
1299 resolve_group_element(element, recursion_depth + 1);
1302 group->element_resolved[group->num_elements_resolved++] = element;
1303 element_info[element].in_group[group_nr] = TRUE;
1310 =============================================================================
1312 -----------------------------------------------------------------------------
1313 initialize game engine due to level / tape version number
1314 =============================================================================
1317 static void InitGameEngine()
1319 int i, j, k, l, x, y;
1321 /* set game engine from tape file when re-playing, else from level file */
1322 game.engine_version = (tape.playing ? tape.engine_version :
1323 level.game_version);
1325 /* ---------------------------------------------------------------------- */
1326 /* set flags for bugs and changes according to active game engine version */
1327 /* ---------------------------------------------------------------------- */
1330 Summary of bugfix/change:
1331 Fixed handling for custom elements that change when pushed by the player.
1333 Fixed/changed in version:
1337 Before 3.1.0, custom elements that "change when pushing" changed directly
1338 after the player started pushing them (until then handled in "DigField()").
1339 Since 3.1.0, these custom elements are not changed until the "pushing"
1340 move of the element is finished (now handled in "ContinueMoving()").
1342 Affected levels/tapes:
1343 The first condition is generally needed for all levels/tapes before version
1344 3.1.0, which might use the old behaviour before it was changed; known tapes
1345 that are affected are some tapes from the level set "Walpurgis Gardens" by
1347 The second condition is an exception from the above case and is needed for
1348 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1349 above (including some development versions of 3.1.0), but before it was
1350 known that this change would break tapes like the above and was fixed in
1351 3.1.1, so that the changed behaviour was active although the engine version
1352 while recording maybe was before 3.1.0. There is at least one tape that is
1353 affected by this exception, which is the tape for the one-level set "Bug
1354 Machine" by Juergen Bonhagen.
1357 game.use_change_when_pushing_bug =
1358 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1360 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1361 tape.game_version < VERSION_IDENT(3,1,1,0)));
1364 Summary of bugfix/change:
1365 Fixed handling for blocking the field the player leaves when moving.
1367 Fixed/changed in version:
1371 Before 3.1.1, when "block last field when moving" was enabled, the field
1372 the player is leaving when moving was blocked for the time of the move,
1373 and was directly unblocked afterwards. This resulted in the last field
1374 being blocked for exactly one less than the number of frames of one player
1375 move. Additionally, even when blocking was disabled, the last field was
1376 blocked for exactly one frame.
1377 Since 3.1.1, due to changes in player movement handling, the last field
1378 is not blocked at all when blocking is disabled. When blocking is enabled,
1379 the last field is blocked for exactly the number of frames of one player
1380 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1381 last field is blocked for exactly one more than the number of frames of
1384 Affected levels/tapes:
1385 (!!! yet to be determined -- probably many !!!)
1388 game.use_block_last_field_bug =
1389 (game.engine_version < VERSION_IDENT(3,1,1,0));
1392 Summary of bugfix/change:
1393 Changed behaviour of CE changes with multiple changes per single frame.
1395 Fixed/changed in version:
1399 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1400 This resulted in race conditions where CEs seem to behave strange in some
1401 situations (where triggered CE changes were just skipped because there was
1402 already a CE change on that tile in the playfield in that engine frame).
1403 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1404 (The number of changes per frame must be limited in any case, because else
1405 it is easily possible to define CE changes that would result in an infinite
1406 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1407 should be set large enough so that it would only be reached in cases where
1408 the corresponding CE change conditions run into a loop. Therefore, it seems
1409 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1410 maximal number of change pages for custom elements.)
1412 Affected levels/tapes:
1416 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1417 game.max_num_changes_per_frame = 1;
1419 game.max_num_changes_per_frame =
1420 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1423 /* ---------------------------------------------------------------------- */
1425 /* default scan direction: scan playfield from top/left to bottom/right */
1426 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1428 /* dynamically adjust element properties according to game engine version */
1429 InitElementPropertiesEngine(game.engine_version);
1432 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1433 printf(" tape version == %06d [%s] [file: %06d]\n",
1434 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1436 printf(" => game.engine_version == %06d\n", game.engine_version);
1440 /* ---------- recursively resolve group elements ------------------------- */
1442 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1443 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1444 element_info[i].in_group[j] = FALSE;
1446 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1447 resolve_group_element(EL_GROUP_START + i, 0);
1450 /* ---------- initialize player's initial move delay --------------------- */
1453 /* dynamically adjust player properties according to level information */
1454 game.initial_move_delay_value =
1455 get_move_delay_from_stepsize(level.initial_player_stepsize);
1457 /* dynamically adjust player properties according to level information */
1458 game.initial_move_delay_value =
1459 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1462 /* dynamically adjust player properties according to game engine version */
1463 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1464 game.initial_move_delay_value : 0);
1466 /* ---------- initialize player's initial push delay --------------------- */
1468 /* dynamically adjust player properties according to game engine version */
1469 game.initial_push_delay_value =
1470 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1472 /* ---------- initialize changing elements ------------------------------- */
1474 /* initialize changing elements information */
1475 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1477 struct ElementInfo *ei = &element_info[i];
1479 /* this pointer might have been changed in the level editor */
1480 ei->change = &ei->change_page[0];
1482 if (!IS_CUSTOM_ELEMENT(i))
1484 ei->change->target_element = EL_EMPTY_SPACE;
1485 ei->change->delay_fixed = 0;
1486 ei->change->delay_random = 0;
1487 ei->change->delay_frames = 1;
1490 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1492 ei->has_change_event[j] = FALSE;
1494 ei->event_page_nr[j] = 0;
1495 ei->event_page[j] = &ei->change_page[0];
1499 /* add changing elements from pre-defined list */
1500 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1502 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1503 struct ElementInfo *ei = &element_info[ch_delay->element];
1505 ei->change->target_element = ch_delay->target_element;
1506 ei->change->delay_fixed = ch_delay->change_delay;
1508 ei->change->pre_change_function = ch_delay->pre_change_function;
1509 ei->change->change_function = ch_delay->change_function;
1510 ei->change->post_change_function = ch_delay->post_change_function;
1512 ei->change->can_change = TRUE;
1513 ei->change->can_change_or_has_action = TRUE;
1515 ei->has_change_event[CE_DELAY] = TRUE;
1517 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1518 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1521 /* ---------- initialize internal run-time variables ------------- */
1523 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1525 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1527 for (j = 0; j < ei->num_change_pages; j++)
1529 ei->change_page[j].can_change_or_has_action =
1530 (ei->change_page[j].can_change |
1531 ei->change_page[j].has_action);
1535 /* add change events from custom element configuration */
1536 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1538 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1540 for (j = 0; j < ei->num_change_pages; j++)
1542 if (!ei->change_page[j].can_change_or_has_action)
1545 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1547 /* only add event page for the first page found with this event */
1548 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1550 ei->has_change_event[k] = TRUE;
1552 ei->event_page_nr[k] = j;
1553 ei->event_page[k] = &ei->change_page[j];
1559 /* ---------- initialize run-time trigger player and element ------------- */
1561 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1563 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1565 for (j = 0; j < ei->num_change_pages; j++)
1567 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1568 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1569 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1570 ei->change_page[j].actual_trigger_ce_value = 0;
1574 /* ---------- initialize trigger events ---------------------------------- */
1576 /* initialize trigger events information */
1577 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1578 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1579 trigger_events[i][j] = FALSE;
1581 /* add trigger events from element change event properties */
1582 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1584 struct ElementInfo *ei = &element_info[i];
1586 for (j = 0; j < ei->num_change_pages; j++)
1588 if (!ei->change_page[j].can_change_or_has_action)
1591 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1593 int trigger_element = ei->change_page[j].trigger_element;
1595 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1597 if (ei->change_page[j].has_event[k])
1599 if (IS_GROUP_ELEMENT(trigger_element))
1601 struct ElementGroupInfo *group =
1602 element_info[trigger_element].group;
1604 for (l = 0; l < group->num_elements_resolved; l++)
1605 trigger_events[group->element_resolved[l]][k] = TRUE;
1608 trigger_events[trigger_element][k] = TRUE;
1615 /* ---------- initialize push delay -------------------------------------- */
1617 /* initialize push delay values to default */
1618 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1620 if (!IS_CUSTOM_ELEMENT(i))
1622 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1623 element_info[i].push_delay_random = game.default_push_delay_random;
1627 /* set push delay value for certain elements from pre-defined list */
1628 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1630 int e = push_delay_list[i].element;
1632 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1633 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1636 /* set push delay value for Supaplex elements for newer engine versions */
1637 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1639 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1641 if (IS_SP_ELEMENT(i))
1643 /* set SP push delay to just enough to push under a falling zonk */
1644 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1646 element_info[i].push_delay_fixed = delay;
1647 element_info[i].push_delay_random = 0;
1652 /* ---------- initialize move stepsize ----------------------------------- */
1654 /* initialize move stepsize values to default */
1655 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1656 if (!IS_CUSTOM_ELEMENT(i))
1657 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1659 /* set move stepsize value for certain elements from pre-defined list */
1660 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1662 int e = move_stepsize_list[i].element;
1664 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1667 /* ---------- initialize collect score ----------------------------------- */
1669 /* initialize collect score values for custom elements from initial value */
1670 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1671 if (IS_CUSTOM_ELEMENT(i))
1672 element_info[i].collect_score = element_info[i].collect_score_initial;
1674 /* ---------- initialize collect count ----------------------------------- */
1676 /* initialize collect count values for non-custom elements */
1677 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1678 if (!IS_CUSTOM_ELEMENT(i))
1679 element_info[i].collect_count_initial = 0;
1681 /* add collect count values for all elements from pre-defined list */
1682 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1683 element_info[collect_count_list[i].element].collect_count_initial =
1684 collect_count_list[i].count;
1686 /* ---------- initialize access direction -------------------------------- */
1688 /* initialize access direction values to default (access from every side) */
1689 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1690 if (!IS_CUSTOM_ELEMENT(i))
1691 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1693 /* set access direction value for certain elements from pre-defined list */
1694 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1695 element_info[access_direction_list[i].element].access_direction =
1696 access_direction_list[i].direction;
1698 /* ---------- initialize explosion content ------------------------------- */
1699 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1701 if (IS_CUSTOM_ELEMENT(i))
1704 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1706 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1708 element_info[i].content.e[x][y] =
1709 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1710 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1711 i == EL_PLAYER_3 ? EL_EMERALD :
1712 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1713 i == EL_MOLE ? EL_EMERALD_RED :
1714 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1715 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1716 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1717 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1718 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1719 i == EL_WALL_EMERALD ? EL_EMERALD :
1720 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1721 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1722 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1723 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1724 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1725 i == EL_WALL_PEARL ? EL_PEARL :
1726 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1732 int get_num_special_action(int element, int action_first, int action_last)
1734 int num_special_action = 0;
1737 for (i = action_first; i <= action_last; i++)
1739 boolean found = FALSE;
1741 for (j = 0; j < NUM_DIRECTIONS; j++)
1742 if (el_act_dir2img(element, i, j) !=
1743 el_act_dir2img(element, ACTION_DEFAULT, j))
1747 num_special_action++;
1752 return num_special_action;
1756 =============================================================================
1758 -----------------------------------------------------------------------------
1759 initialize and start new game
1760 =============================================================================
1765 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1766 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1767 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1772 /* don't play tapes over network */
1773 network_playing = (options.network && !tape.playing);
1775 for (i = 0; i < MAX_PLAYERS; i++)
1777 struct PlayerInfo *player = &stored_player[i];
1779 player->index_nr = i;
1780 player->index_bit = (1 << i);
1781 player->element_nr = EL_PLAYER_1 + i;
1783 player->present = FALSE;
1784 player->active = FALSE;
1787 player->effective_action = 0;
1788 player->programmed_action = 0;
1791 player->gems_still_needed = level.gems_needed;
1792 player->sokobanfields_still_needed = 0;
1793 player->lights_still_needed = 0;
1794 player->friends_still_needed = 0;
1796 for (j = 0; j < MAX_NUM_KEYS; j++)
1797 player->key[j] = FALSE;
1799 player->dynabomb_count = 0;
1800 player->dynabomb_size = 1;
1801 player->dynabombs_left = 0;
1802 player->dynabomb_xl = FALSE;
1804 player->MovDir = MV_NONE;
1807 player->GfxDir = MV_NONE;
1808 player->GfxAction = ACTION_DEFAULT;
1810 player->StepFrame = 0;
1812 player->use_murphy = FALSE;
1813 player->artwork_element =
1814 (level.use_artwork_element[i] ? level.artwork_element[i] :
1815 player->element_nr);
1817 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1818 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1820 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1822 player->actual_frame_counter = 0;
1824 player->step_counter = 0;
1826 player->last_move_dir = MV_NONE;
1828 player->is_waiting = FALSE;
1829 player->is_moving = FALSE;
1830 player->is_auto_moving = FALSE;
1831 player->is_digging = FALSE;
1832 player->is_snapping = FALSE;
1833 player->is_collecting = FALSE;
1834 player->is_pushing = FALSE;
1835 player->is_switching = FALSE;
1836 player->is_dropping = FALSE;
1838 player->is_bored = FALSE;
1839 player->is_sleeping = FALSE;
1841 player->frame_counter_bored = -1;
1842 player->frame_counter_sleeping = -1;
1844 player->anim_delay_counter = 0;
1845 player->post_delay_counter = 0;
1847 player->action_waiting = ACTION_DEFAULT;
1848 player->last_action_waiting = ACTION_DEFAULT;
1849 player->special_action_bored = ACTION_DEFAULT;
1850 player->special_action_sleeping = ACTION_DEFAULT;
1852 /* set number of special actions for bored and sleeping animation */
1853 player->num_special_action_bored =
1854 get_num_special_action(player->artwork_element,
1855 ACTION_BORING_1, ACTION_BORING_LAST);
1856 player->num_special_action_sleeping =
1857 get_num_special_action(player->artwork_element,
1858 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1860 player->switch_x = -1;
1861 player->switch_y = -1;
1863 player->drop_x = -1;
1864 player->drop_y = -1;
1866 player->show_envelope = 0;
1869 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1871 player->move_delay = game.initial_move_delay;
1872 player->move_delay_value = game.initial_move_delay_value;
1874 player->move_delay_value_next = -1;
1876 player->move_delay_reset_counter = 0;
1878 player->cannot_move = FALSE;
1881 player->push_delay = -1; /* initialized when pushing starts */
1882 player->push_delay_value = game.initial_push_delay_value;
1884 player->drop_delay = 0;
1886 player->last_jx = player->last_jy = 0;
1887 player->jx = player->jy = 0;
1889 player->shield_normal_time_left = 0;
1890 player->shield_deadly_time_left = 0;
1892 player->inventory_infinite_element = EL_UNDEFINED;
1893 player->inventory_size = 0;
1895 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1896 SnapField(player, 0, 0);
1898 player->LevelSolved = FALSE;
1899 player->GameOver = FALSE;
1902 network_player_action_received = FALSE;
1904 #if defined(NETWORK_AVALIABLE)
1905 /* initial null action */
1906 if (network_playing)
1907 SendToServer_MovePlayer(MV_NONE);
1916 TimeLeft = level.time;
1919 ScreenMovDir = MV_NONE;
1923 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1925 AllPlayersGone = FALSE;
1927 game.yamyam_content_nr = 0;
1928 game.magic_wall_active = FALSE;
1929 game.magic_wall_time_left = 0;
1930 game.light_time_left = 0;
1931 game.timegate_time_left = 0;
1932 game.switchgate_pos = 0;
1933 game.wind_direction = level.wind_direction_initial;
1934 game.gravity = level.initial_gravity;
1935 game.explosions_delayed = TRUE;
1937 game.lenses_time_left = 0;
1938 game.magnify_time_left = 0;
1940 game.ball_state = level.ball_state_initial;
1941 game.ball_content_nr = 0;
1943 game.envelope_active = FALSE;
1945 game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
1947 for (i = 0; i < NUM_BELTS; i++)
1949 game.belt_dir[i] = MV_NONE;
1950 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1953 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1954 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1957 SCAN_PLAYFIELD(x, y)
1959 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1962 Feld[x][y] = level.field[x][y];
1963 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1964 ChangeDelay[x][y] = 0;
1965 ChangePage[x][y] = -1;
1966 #if USE_NEW_CUSTOM_VALUE
1967 CustomValue[x][y] = 0; /* initialized in InitField() */
1969 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1971 WasJustMoving[x][y] = 0;
1972 WasJustFalling[x][y] = 0;
1973 CheckCollision[x][y] = 0;
1975 Pushed[x][y] = FALSE;
1977 ChangeCount[x][y] = 0;
1978 ChangeEvent[x][y] = -1;
1980 ExplodePhase[x][y] = 0;
1981 ExplodeDelay[x][y] = 0;
1982 ExplodeField[x][y] = EX_TYPE_NONE;
1984 RunnerVisit[x][y] = 0;
1985 PlayerVisit[x][y] = 0;
1988 GfxRandom[x][y] = INIT_GFX_RANDOM();
1989 GfxElement[x][y] = EL_UNDEFINED;
1990 GfxAction[x][y] = ACTION_DEFAULT;
1991 GfxDir[x][y] = MV_NONE;
1995 SCAN_PLAYFIELD(x, y)
1997 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2000 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2002 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2004 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2007 InitField(x, y, TRUE);
2012 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2013 emulate_sb ? EMU_SOKOBAN :
2014 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2016 #if USE_NEW_ALL_SLIPPERY
2017 /* initialize type of slippery elements */
2018 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2020 if (!IS_CUSTOM_ELEMENT(i))
2022 /* default: elements slip down either to the left or right randomly */
2023 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2025 /* SP style elements prefer to slip down on the left side */
2026 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2027 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2029 /* BD style elements prefer to slip down on the left side */
2030 if (game.emulation == EMU_BOULDERDASH)
2031 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2036 /* initialize explosion and ignition delay */
2037 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2039 if (!IS_CUSTOM_ELEMENT(i))
2042 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2043 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2044 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2045 int last_phase = (num_phase + 1) * delay;
2046 int half_phase = (num_phase / 2) * delay;
2048 element_info[i].explosion_delay = last_phase - 1;
2049 element_info[i].ignition_delay = half_phase;
2051 if (i == EL_BLACK_ORB)
2052 element_info[i].ignition_delay = 1;
2056 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2057 element_info[i].explosion_delay = 1;
2059 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2060 element_info[i].ignition_delay = 1;
2064 /* correct non-moving belts to start moving left */
2065 for (i = 0; i < NUM_BELTS; i++)
2066 if (game.belt_dir[i] == MV_NONE)
2067 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2069 /* check if any connected player was not found in playfield */
2070 for (i = 0; i < MAX_PLAYERS; i++)
2072 struct PlayerInfo *player = &stored_player[i];
2074 if (player->connected && !player->present)
2076 for (j = 0; j < MAX_PLAYERS; j++)
2078 struct PlayerInfo *some_player = &stored_player[j];
2079 int jx = some_player->jx, jy = some_player->jy;
2081 /* assign first free player found that is present in the playfield */
2082 if (some_player->present && !some_player->connected)
2084 player->present = TRUE;
2085 player->active = TRUE;
2087 some_player->present = FALSE;
2088 some_player->active = FALSE;
2091 player->element_nr = some_player->element_nr;
2094 player->artwork_element = some_player->artwork_element;
2096 player->block_last_field = some_player->block_last_field;
2097 player->block_delay_adjustment = some_player->block_delay_adjustment;
2099 StorePlayer[jx][jy] = player->element_nr;
2100 player->jx = player->last_jx = jx;
2101 player->jy = player->last_jy = jy;
2111 /* when playing a tape, eliminate all players who do not participate */
2113 for (i = 0; i < MAX_PLAYERS; i++)
2115 if (stored_player[i].active && !tape.player_participates[i])
2117 struct PlayerInfo *player = &stored_player[i];
2118 int jx = player->jx, jy = player->jy;
2120 player->active = FALSE;
2121 StorePlayer[jx][jy] = 0;
2122 Feld[jx][jy] = EL_EMPTY;
2126 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2128 /* when in single player mode, eliminate all but the first active player */
2130 for (i = 0; i < MAX_PLAYERS; i++)
2132 if (stored_player[i].active)
2134 for (j = i + 1; j < MAX_PLAYERS; j++)
2136 if (stored_player[j].active)
2138 struct PlayerInfo *player = &stored_player[j];
2139 int jx = player->jx, jy = player->jy;
2141 player->active = FALSE;
2142 player->present = FALSE;
2144 StorePlayer[jx][jy] = 0;
2145 Feld[jx][jy] = EL_EMPTY;
2152 /* when recording the game, store which players take part in the game */
2155 for (i = 0; i < MAX_PLAYERS; i++)
2156 if (stored_player[i].active)
2157 tape.player_participates[i] = TRUE;
2162 for (i = 0; i < MAX_PLAYERS; i++)
2164 struct PlayerInfo *player = &stored_player[i];
2166 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2171 if (local_player == player)
2172 printf("Player %d is local player.\n", i+1);
2176 if (BorderElement == EL_EMPTY)
2179 SBX_Right = lev_fieldx - SCR_FIELDX;
2181 SBY_Lower = lev_fieldy - SCR_FIELDY;
2186 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2188 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2191 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2192 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2194 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2195 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2197 /* if local player not found, look for custom element that might create
2198 the player (make some assumptions about the right custom element) */
2199 if (!local_player->present)
2201 int start_x = 0, start_y = 0;
2202 int found_rating = 0;
2203 int found_element = EL_UNDEFINED;
2204 int player_nr = local_player->index_nr;
2207 SCAN_PLAYFIELD(x, y)
2209 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2212 int element = Feld[x][y];
2217 if (level.use_start_element[player_nr] &&
2218 level.start_element[player_nr] == element &&
2225 found_element = element;
2228 if (!IS_CUSTOM_ELEMENT(element))
2231 if (CAN_CHANGE(element))
2233 for (i = 0; i < element_info[element].num_change_pages; i++)
2235 /* check for player created from custom element as single target */
2236 content = element_info[element].change_page[i].target_element;
2237 is_player = ELEM_IS_PLAYER(content);
2239 if (is_player && (found_rating < 3 || element < found_element))
2245 found_element = element;
2250 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2252 /* check for player created from custom element as explosion content */
2253 content = element_info[element].content.e[xx][yy];
2254 is_player = ELEM_IS_PLAYER(content);
2256 if (is_player && (found_rating < 2 || element < found_element))
2258 start_x = x + xx - 1;
2259 start_y = y + yy - 1;
2262 found_element = element;
2265 if (!CAN_CHANGE(element))
2268 for (i = 0; i < element_info[element].num_change_pages; i++)
2270 /* check for player created from custom element as extended target */
2272 element_info[element].change_page[i].target_content.e[xx][yy];
2274 is_player = ELEM_IS_PLAYER(content);
2276 if (is_player && (found_rating < 1 || element < found_element))
2278 start_x = x + xx - 1;
2279 start_y = y + yy - 1;
2282 found_element = element;
2288 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2289 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2292 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2293 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2298 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2299 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2300 local_player->jx - MIDPOSX);
2302 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2303 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2304 local_player->jy - MIDPOSY);
2307 if (!game.restart_level)
2308 CloseDoor(DOOR_CLOSE_1);
2310 /* !!! FIX THIS (START) !!! */
2311 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2313 InitGameEngine_EM();
2320 /* after drawing the level, correct some elements */
2321 if (game.timegate_time_left == 0)
2322 CloseAllOpenTimegates();
2324 if (setup.soft_scrolling)
2325 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2327 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2330 /* !!! FIX THIS (END) !!! */
2332 if (!game.restart_level)
2334 /* copy default game door content to main double buffer */
2335 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2336 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2339 DrawGameDoorValues();
2341 if (!game.restart_level)
2345 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2346 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2347 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2351 /* copy actual game door content to door double buffer for OpenDoor() */
2352 BlitBitmap(drawto, bitmap_db_door,
2353 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2355 OpenDoor(DOOR_OPEN_ALL);
2357 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2359 if (setup.sound_music)
2362 KeyboardAutoRepeatOffUnlessAutoplay();
2366 for (i = 0; i < MAX_PLAYERS; i++)
2367 printf("Player %d %sactive.\n",
2368 i + 1, (stored_player[i].active ? "" : "not "));
2372 game.restart_level = FALSE;
2375 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2377 /* this is used for non-R'n'D game engines to update certain engine values */
2379 /* needed to determine if sounds are played within the visible screen area */
2380 scroll_x = actual_scroll_x;
2381 scroll_y = actual_scroll_y;
2384 void InitMovDir(int x, int y)
2386 int i, element = Feld[x][y];
2387 static int xy[4][2] =
2394 static int direction[3][4] =
2396 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2397 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2398 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2407 Feld[x][y] = EL_BUG;
2408 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2411 case EL_SPACESHIP_RIGHT:
2412 case EL_SPACESHIP_UP:
2413 case EL_SPACESHIP_LEFT:
2414 case EL_SPACESHIP_DOWN:
2415 Feld[x][y] = EL_SPACESHIP;
2416 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2419 case EL_BD_BUTTERFLY_RIGHT:
2420 case EL_BD_BUTTERFLY_UP:
2421 case EL_BD_BUTTERFLY_LEFT:
2422 case EL_BD_BUTTERFLY_DOWN:
2423 Feld[x][y] = EL_BD_BUTTERFLY;
2424 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2427 case EL_BD_FIREFLY_RIGHT:
2428 case EL_BD_FIREFLY_UP:
2429 case EL_BD_FIREFLY_LEFT:
2430 case EL_BD_FIREFLY_DOWN:
2431 Feld[x][y] = EL_BD_FIREFLY;
2432 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2435 case EL_PACMAN_RIGHT:
2437 case EL_PACMAN_LEFT:
2438 case EL_PACMAN_DOWN:
2439 Feld[x][y] = EL_PACMAN;
2440 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2443 case EL_SP_SNIKSNAK:
2444 MovDir[x][y] = MV_UP;
2447 case EL_SP_ELECTRON:
2448 MovDir[x][y] = MV_LEFT;
2455 Feld[x][y] = EL_MOLE;
2456 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2460 if (IS_CUSTOM_ELEMENT(element))
2462 struct ElementInfo *ei = &element_info[element];
2463 int move_direction_initial = ei->move_direction_initial;
2464 int move_pattern = ei->move_pattern;
2466 if (move_direction_initial == MV_START_PREVIOUS)
2468 if (MovDir[x][y] != MV_NONE)
2471 move_direction_initial = MV_START_AUTOMATIC;
2474 if (move_direction_initial == MV_START_RANDOM)
2475 MovDir[x][y] = 1 << RND(4);
2476 else if (move_direction_initial & MV_ANY_DIRECTION)
2477 MovDir[x][y] = move_direction_initial;
2478 else if (move_pattern == MV_ALL_DIRECTIONS ||
2479 move_pattern == MV_TURNING_LEFT ||
2480 move_pattern == MV_TURNING_RIGHT ||
2481 move_pattern == MV_TURNING_LEFT_RIGHT ||
2482 move_pattern == MV_TURNING_RIGHT_LEFT ||
2483 move_pattern == MV_TURNING_RANDOM)
2484 MovDir[x][y] = 1 << RND(4);
2485 else if (move_pattern == MV_HORIZONTAL)
2486 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2487 else if (move_pattern == MV_VERTICAL)
2488 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2489 else if (move_pattern & MV_ANY_DIRECTION)
2490 MovDir[x][y] = element_info[element].move_pattern;
2491 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2492 move_pattern == MV_ALONG_RIGHT_SIDE)
2494 /* use random direction as default start direction */
2495 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2496 MovDir[x][y] = 1 << RND(4);
2498 for (i = 0; i < NUM_DIRECTIONS; i++)
2500 int x1 = x + xy[i][0];
2501 int y1 = y + xy[i][1];
2503 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2505 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2506 MovDir[x][y] = direction[0][i];
2508 MovDir[x][y] = direction[1][i];
2517 MovDir[x][y] = 1 << RND(4);
2519 if (element != EL_BUG &&
2520 element != EL_SPACESHIP &&
2521 element != EL_BD_BUTTERFLY &&
2522 element != EL_BD_FIREFLY)
2525 for (i = 0; i < NUM_DIRECTIONS; i++)
2527 int x1 = x + xy[i][0];
2528 int y1 = y + xy[i][1];
2530 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2532 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2534 MovDir[x][y] = direction[0][i];
2537 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2538 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2540 MovDir[x][y] = direction[1][i];
2549 GfxDir[x][y] = MovDir[x][y];
2552 void InitAmoebaNr(int x, int y)
2555 int group_nr = AmoebeNachbarNr(x, y);
2559 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2561 if (AmoebaCnt[i] == 0)
2569 AmoebaNr[x][y] = group_nr;
2570 AmoebaCnt[group_nr]++;
2571 AmoebaCnt2[group_nr]++;
2577 boolean raise_level = FALSE;
2579 if (local_player->MovPos)
2582 if (tape.auto_play) /* tape might already be stopped here */
2583 tape.auto_play_level_solved = TRUE;
2585 local_player->LevelSolved = FALSE;
2587 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2591 if (!tape.playing && setup.sound_loops)
2592 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2593 SND_CTRL_PLAY_LOOP);
2595 while (TimeLeft > 0)
2597 if (!tape.playing && !setup.sound_loops)
2598 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2600 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2603 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2608 RaiseScore(level.score[SC_TIME_BONUS]);
2611 DrawGameValue_Time(TimeLeft);
2619 if (!tape.playing && setup.sound_loops)
2620 StopSound(SND_GAME_LEVELTIME_BONUS);
2622 else if (level.time == 0) /* level without time limit */
2624 if (!tape.playing && setup.sound_loops)
2625 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2626 SND_CTRL_PLAY_LOOP);
2628 while (TimePlayed < 999)
2630 if (!tape.playing && !setup.sound_loops)
2631 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2633 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2636 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2641 RaiseScore(level.score[SC_TIME_BONUS]);
2644 DrawGameValue_Time(TimePlayed);
2652 if (!tape.playing && setup.sound_loops)
2653 StopSound(SND_GAME_LEVELTIME_BONUS);
2656 /* close exit door after last player */
2657 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2658 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2659 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2661 int element = Feld[ExitX][ExitY];
2663 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2664 EL_SP_EXIT_CLOSING);
2666 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2669 /* player disappears */
2670 if (ExitX >= 0 && ExitY >= 0)
2671 DrawLevelField(ExitX, ExitY);
2677 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2679 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2685 CloseDoor(DOOR_CLOSE_1);
2690 SaveTape(tape.level_nr); /* Ask to save tape */
2693 if (level_nr == leveldir_current->handicap_level)
2695 leveldir_current->handicap_level++;
2696 SaveLevelSetup_SeriesInfo();
2699 if (level_editor_test_game)
2700 local_player->score = -1; /* no highscore when playing from editor */
2701 else if (level_nr < leveldir_current->last_level)
2702 raise_level = TRUE; /* advance to next level */
2704 if ((hi_pos = NewHiScore()) >= 0)
2706 game_status = GAME_MODE_SCORES;
2707 DrawHallOfFame(hi_pos);
2716 game_status = GAME_MODE_MAIN;
2733 LoadScore(level_nr);
2735 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2736 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2739 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2741 if (local_player->score > highscore[k].Score)
2743 /* player has made it to the hall of fame */
2745 if (k < MAX_SCORE_ENTRIES - 1)
2747 int m = MAX_SCORE_ENTRIES - 1;
2750 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2751 if (!strcmp(setup.player_name, highscore[l].Name))
2753 if (m == k) /* player's new highscore overwrites his old one */
2757 for (l = m; l > k; l--)
2759 strcpy(highscore[l].Name, highscore[l - 1].Name);
2760 highscore[l].Score = highscore[l - 1].Score;
2767 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2768 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2769 highscore[k].Score = local_player->score;
2775 else if (!strncmp(setup.player_name, highscore[k].Name,
2776 MAX_PLAYER_NAME_LEN))
2777 break; /* player already there with a higher score */
2783 SaveScore(level_nr);
2788 inline static int getElementMoveStepsize(int x, int y)
2790 int element = Feld[x][y];
2791 int direction = MovDir[x][y];
2792 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2793 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2794 int horiz_move = (dx != 0);
2795 int sign = (horiz_move ? dx : dy);
2796 int step = sign * element_info[element].move_stepsize;
2798 /* special values for move stepsize for spring and things on conveyor belt */
2802 if (element == EL_SPRING)
2803 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2804 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2805 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2806 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2808 if (CAN_FALL(element) &&
2809 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2810 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2811 else if (element == EL_SPRING)
2812 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2819 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2821 if (player->GfxAction != action || player->GfxDir != dir)
2824 printf("Player frame reset! (%d => %d, %d => %d)\n",
2825 player->GfxAction, action, player->GfxDir, dir);
2828 player->GfxAction = action;
2829 player->GfxDir = dir;
2831 player->StepFrame = 0;
2835 static void ResetRandomAnimationValue(int x, int y)
2837 GfxRandom[x][y] = INIT_GFX_RANDOM();
2840 static void ResetGfxAnimation(int x, int y)
2843 int element, graphic;
2847 GfxAction[x][y] = ACTION_DEFAULT;
2848 GfxDir[x][y] = MovDir[x][y];
2851 element = Feld[x][y];
2852 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2854 if (graphic_info[graphic].anim_global_sync)
2855 GfxFrame[x][y] = FrameCounter;
2856 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2857 GfxFrame[x][y] = CustomValue[x][y];
2858 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2859 GfxFrame[x][y] = element_info[element].collect_score;
2863 void InitMovingField(int x, int y, int direction)
2865 int element = Feld[x][y];
2869 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2870 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2874 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2875 ResetGfxAnimation(x, y);
2877 MovDir[x][y] = direction;
2878 GfxDir[x][y] = direction;
2879 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2880 ACTION_FALLING : ACTION_MOVING);
2883 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2885 if (graphic_info[graphic].anim_global_sync)
2886 GfxFrame[x][y] = FrameCounter;
2887 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2888 GfxFrame[x][y] = CustomValue[x][y];
2889 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2890 GfxFrame[x][y] = element_info[element].collect_score;
2893 /* this is needed for CEs with property "can move" / "not moving" */
2895 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2897 if (Feld[newx][newy] == EL_EMPTY)
2898 Feld[newx][newy] = EL_BLOCKED;
2900 MovDir[newx][newy] = MovDir[x][y];
2902 #if USE_NEW_CUSTOM_VALUE
2903 CustomValue[newx][newy] = CustomValue[x][y];
2906 GfxFrame[newx][newy] = GfxFrame[x][y];
2907 GfxRandom[newx][newy] = GfxRandom[x][y];
2908 GfxAction[newx][newy] = GfxAction[x][y];
2909 GfxDir[newx][newy] = GfxDir[x][y];
2913 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2915 int direction = MovDir[x][y];
2917 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2918 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2920 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2921 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2928 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2930 int oldx = x, oldy = y;
2931 int direction = MovDir[x][y];
2933 if (direction == MV_LEFT)
2935 else if (direction == MV_RIGHT)
2937 else if (direction == MV_UP)
2939 else if (direction == MV_DOWN)
2942 *comes_from_x = oldx;
2943 *comes_from_y = oldy;
2946 int MovingOrBlocked2Element(int x, int y)
2948 int element = Feld[x][y];
2950 if (element == EL_BLOCKED)
2954 Blocked2Moving(x, y, &oldx, &oldy);
2955 return Feld[oldx][oldy];
2961 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2963 /* like MovingOrBlocked2Element(), but if element is moving
2964 and (x,y) is the field the moving element is just leaving,
2965 return EL_BLOCKED instead of the element value */
2966 int element = Feld[x][y];
2968 if (IS_MOVING(x, y))
2970 if (element == EL_BLOCKED)
2974 Blocked2Moving(x, y, &oldx, &oldy);
2975 return Feld[oldx][oldy];
2984 static void RemoveField(int x, int y)
2986 Feld[x][y] = EL_EMPTY;
2992 #if USE_NEW_CUSTOM_VALUE
2993 CustomValue[x][y] = 0;
2997 ChangeDelay[x][y] = 0;
2998 ChangePage[x][y] = -1;
2999 Pushed[x][y] = FALSE;
3002 ExplodeField[x][y] = EX_TYPE_NONE;
3005 GfxElement[x][y] = EL_UNDEFINED;
3006 GfxAction[x][y] = ACTION_DEFAULT;
3007 GfxDir[x][y] = MV_NONE;
3010 void RemoveMovingField(int x, int y)
3012 int oldx = x, oldy = y, newx = x, newy = y;
3013 int element = Feld[x][y];
3014 int next_element = EL_UNDEFINED;
3016 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3019 if (IS_MOVING(x, y))
3021 Moving2Blocked(x, y, &newx, &newy);
3023 if (Feld[newx][newy] != EL_BLOCKED)
3025 /* element is moving, but target field is not free (blocked), but
3026 already occupied by something different (example: acid pool);
3027 in this case, only remove the moving field, but not the target */
3029 RemoveField(oldx, oldy);
3031 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3033 DrawLevelField(oldx, oldy);
3038 else if (element == EL_BLOCKED)
3040 Blocked2Moving(x, y, &oldx, &oldy);
3041 if (!IS_MOVING(oldx, oldy))
3045 if (element == EL_BLOCKED &&
3046 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3047 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3048 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3049 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3050 next_element = get_next_element(Feld[oldx][oldy]);
3052 RemoveField(oldx, oldy);
3053 RemoveField(newx, newy);
3055 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3057 if (next_element != EL_UNDEFINED)
3058 Feld[oldx][oldy] = next_element;
3060 DrawLevelField(oldx, oldy);
3061 DrawLevelField(newx, newy);
3064 void DrawDynamite(int x, int y)
3066 int sx = SCREENX(x), sy = SCREENY(y);
3067 int graphic = el2img(Feld[x][y]);
3070 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3073 if (IS_WALKABLE_INSIDE(Back[x][y]))
3077 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3078 else if (Store[x][y])
3079 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3081 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3083 if (Back[x][y] || Store[x][y])
3084 DrawGraphicThruMask(sx, sy, graphic, frame);
3086 DrawGraphic(sx, sy, graphic, frame);
3089 void CheckDynamite(int x, int y)
3091 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3095 if (MovDelay[x][y] != 0)
3098 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3104 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3109 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3111 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3112 boolean no_delay = (tape.warp_forward);
3113 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3114 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3115 int jx = player->jx;
3116 int jy = player->jy;
3118 if (quick_relocation)
3120 int offset = (setup.scroll_delay ? 3 : 0);
3122 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3124 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3125 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3126 player->jx - MIDPOSX);
3128 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3129 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3130 player->jy - MIDPOSY);
3134 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3135 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3136 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3138 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3139 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3140 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3142 /* don't scroll over playfield boundaries */
3143 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3144 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3146 /* don't scroll over playfield boundaries */
3147 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3148 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3151 RedrawPlayfield(TRUE, 0,0,0,0);
3155 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3156 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3157 player->jx - MIDPOSX);
3159 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3160 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3161 player->jy - MIDPOSY);
3163 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3165 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3168 int fx = FX, fy = FY;
3170 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3171 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3173 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3179 fx += dx * TILEX / 2;
3180 fy += dy * TILEY / 2;
3182 ScrollLevel(dx, dy);
3185 /* scroll in two steps of half tile size to make things smoother */
3186 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3188 Delay(wait_delay_value);
3190 /* scroll second step to align at full tile size */
3192 Delay(wait_delay_value);
3197 Delay(wait_delay_value);
3201 void RelocatePlayer(int jx, int jy, int el_player_raw)
3203 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3204 int player_nr = GET_PLAYER_NR(el_player);
3205 struct PlayerInfo *player = &stored_player[player_nr];
3206 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3207 boolean no_delay = (tape.warp_forward);
3208 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3209 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3210 int old_jx = player->jx;
3211 int old_jy = player->jy;
3212 int old_element = Feld[old_jx][old_jy];
3213 int element = Feld[jx][jy];
3214 boolean player_relocated = (old_jx != jx || old_jy != jy);
3216 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3217 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3218 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3219 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3220 int leave_side_horiz = move_dir_horiz;
3221 int leave_side_vert = move_dir_vert;
3222 int enter_side = enter_side_horiz | enter_side_vert;
3223 int leave_side = leave_side_horiz | leave_side_vert;
3225 if (player->GameOver) /* do not reanimate dead player */
3228 if (!player_relocated) /* no need to relocate the player */
3231 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3233 RemoveField(jx, jy); /* temporarily remove newly placed player */
3234 DrawLevelField(jx, jy);
3237 if (player->present)
3239 while (player->MovPos)
3241 ScrollPlayer(player, SCROLL_GO_ON);
3242 ScrollScreen(NULL, SCROLL_GO_ON);
3244 AdvanceFrameAndPlayerCounters(player->index_nr);
3249 Delay(wait_delay_value);
3252 DrawPlayer(player); /* needed here only to cleanup last field */
3253 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3255 player->is_moving = FALSE;
3258 if (IS_CUSTOM_ELEMENT(old_element))
3259 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3261 player->index_bit, leave_side);
3263 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3265 player->index_bit, leave_side);
3267 Feld[jx][jy] = el_player;
3268 InitPlayerField(jx, jy, el_player, TRUE);
3270 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3272 Feld[jx][jy] = element;
3273 InitField(jx, jy, FALSE);
3277 /* only visually relocate centered player */
3278 if (player->index_nr == game.centered_player_nr)
3279 DrawRelocatePlayer(player, level.instant_relocation);
3281 if (player == local_player) /* only visually relocate local player */
3282 DrawRelocatePlayer(player, level.instant_relocation);
3285 TestIfPlayerTouchesBadThing(jx, jy);
3286 TestIfPlayerTouchesCustomElement(jx, jy);
3288 if (IS_CUSTOM_ELEMENT(element))
3289 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3290 player->index_bit, enter_side);
3292 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3293 player->index_bit, enter_side);
3296 void Explode(int ex, int ey, int phase, int mode)
3302 /* !!! eliminate this variable !!! */
3303 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3305 if (game.explosions_delayed)
3307 ExplodeField[ex][ey] = mode;
3311 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3313 int center_element = Feld[ex][ey];
3314 int artwork_element, explosion_element; /* set these values later */
3317 /* --- This is only really needed (and now handled) in "Impact()". --- */
3318 /* do not explode moving elements that left the explode field in time */
3319 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3320 center_element == EL_EMPTY &&
3321 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3326 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3327 if (mode == EX_TYPE_NORMAL ||
3328 mode == EX_TYPE_CENTER ||
3329 mode == EX_TYPE_CROSS)
3330 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3333 /* remove things displayed in background while burning dynamite */
3334 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3337 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3339 /* put moving element to center field (and let it explode there) */
3340 center_element = MovingOrBlocked2Element(ex, ey);
3341 RemoveMovingField(ex, ey);
3342 Feld[ex][ey] = center_element;
3345 /* now "center_element" is finally determined -- set related values now */
3346 artwork_element = center_element; /* for custom player artwork */
3347 explosion_element = center_element; /* for custom player artwork */
3349 if (IS_PLAYER(ex, ey))
3351 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3353 artwork_element = stored_player[player_nr].artwork_element;
3355 if (level.use_explosion_element[player_nr])
3357 explosion_element = level.explosion_element[player_nr];
3358 artwork_element = explosion_element;
3363 if (mode == EX_TYPE_NORMAL ||
3364 mode == EX_TYPE_CENTER ||
3365 mode == EX_TYPE_CROSS)
3366 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3370 last_phase = element_info[explosion_element].explosion_delay + 1;
3372 last_phase = element_info[center_element].explosion_delay + 1;
3375 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3377 int xx = x - ex + 1;
3378 int yy = y - ey + 1;
3381 if (!IN_LEV_FIELD(x, y) ||
3382 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3383 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3386 element = Feld[x][y];
3388 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3390 element = MovingOrBlocked2Element(x, y);
3392 if (!IS_EXPLOSION_PROOF(element))
3393 RemoveMovingField(x, y);
3396 /* indestructible elements can only explode in center (but not flames) */
3397 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3398 mode == EX_TYPE_BORDER)) ||
3399 element == EL_FLAMES)
3402 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3403 behaviour, for example when touching a yamyam that explodes to rocks
3404 with active deadly shield, a rock is created under the player !!! */
3405 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3407 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3408 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3409 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3411 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3414 if (IS_ACTIVE_BOMB(element))
3416 /* re-activate things under the bomb like gate or penguin */
3417 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3424 /* save walkable background elements while explosion on same tile */
3425 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3426 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3427 Back[x][y] = element;
3429 /* ignite explodable elements reached by other explosion */
3430 if (element == EL_EXPLOSION)
3431 element = Store2[x][y];
3433 if (AmoebaNr[x][y] &&
3434 (element == EL_AMOEBA_FULL ||
3435 element == EL_BD_AMOEBA ||
3436 element == EL_AMOEBA_GROWING))
3438 AmoebaCnt[AmoebaNr[x][y]]--;
3439 AmoebaCnt2[AmoebaNr[x][y]]--;
3444 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3447 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3449 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3451 switch(StorePlayer[ex][ey])
3454 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3457 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3460 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3464 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3469 if (PLAYERINFO(ex, ey)->use_murphy)
3470 Store[x][y] = EL_EMPTY;
3473 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3474 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3475 else if (ELEM_IS_PLAYER(center_element))
3476 Store[x][y] = EL_EMPTY;
3477 else if (center_element == EL_YAMYAM)
3478 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3479 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3480 Store[x][y] = element_info[center_element].content.e[xx][yy];
3482 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3483 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3484 otherwise) -- FIX THIS !!! */
3485 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3486 Store[x][y] = element_info[element].content.e[1][1];
3488 else if (!CAN_EXPLODE(element))
3489 Store[x][y] = element_info[element].content.e[1][1];
3492 Store[x][y] = EL_EMPTY;
3494 else if (center_element == EL_MOLE)
3495 Store[x][y] = EL_EMERALD_RED;
3496 else if (center_element == EL_PENGUIN)
3497 Store[x][y] = EL_EMERALD_PURPLE;
3498 else if (center_element == EL_BUG)
3499 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3500 else if (center_element == EL_BD_BUTTERFLY)
3501 Store[x][y] = EL_BD_DIAMOND;
3502 else if (center_element == EL_SP_ELECTRON)
3503 Store[x][y] = EL_SP_INFOTRON;
3504 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3505 Store[x][y] = level.amoeba_content;
3506 else if (center_element == EL_YAMYAM)
3507 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3508 else if (IS_CUSTOM_ELEMENT(center_element) &&
3509 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3510 Store[x][y] = element_info[center_element].content.e[xx][yy];
3511 else if (element == EL_WALL_EMERALD)
3512 Store[x][y] = EL_EMERALD;
3513 else if (element == EL_WALL_DIAMOND)
3514 Store[x][y] = EL_DIAMOND;
3515 else if (element == EL_WALL_BD_DIAMOND)
3516 Store[x][y] = EL_BD_DIAMOND;
3517 else if (element == EL_WALL_EMERALD_YELLOW)
3518 Store[x][y] = EL_EMERALD_YELLOW;
3519 else if (element == EL_WALL_EMERALD_RED)
3520 Store[x][y] = EL_EMERALD_RED;
3521 else if (element == EL_WALL_EMERALD_PURPLE)
3522 Store[x][y] = EL_EMERALD_PURPLE;
3523 else if (element == EL_WALL_PEARL)
3524 Store[x][y] = EL_PEARL;
3525 else if (element == EL_WALL_CRYSTAL)
3526 Store[x][y] = EL_CRYSTAL;
3527 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3528 Store[x][y] = element_info[element].content.e[1][1];
3530 Store[x][y] = EL_EMPTY;
3533 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3534 center_element == EL_AMOEBA_TO_DIAMOND)
3535 Store2[x][y] = element;
3537 Feld[x][y] = EL_EXPLOSION;
3538 GfxElement[x][y] = artwork_element;
3541 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3542 x, y, artwork_element, EL_NAME(artwork_element));
3545 ExplodePhase[x][y] = 1;
3546 ExplodeDelay[x][y] = last_phase;
3551 if (center_element == EL_YAMYAM)
3552 game.yamyam_content_nr =
3553 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3565 GfxFrame[x][y] = 0; /* restart explosion animation */
3567 last_phase = ExplodeDelay[x][y];
3569 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3573 /* activate this even in non-DEBUG version until cause for crash in
3574 getGraphicAnimationFrame() (see below) is found and eliminated */
3580 /* this can happen if the player leaves an explosion just in time */
3581 if (GfxElement[x][y] == EL_UNDEFINED)
3582 GfxElement[x][y] = EL_EMPTY;
3584 if (GfxElement[x][y] == EL_UNDEFINED)
3587 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3588 printf("Explode(): This should never happen!\n");
3591 GfxElement[x][y] = EL_EMPTY;
3597 border_element = Store2[x][y];
3598 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3599 border_element = StorePlayer[x][y];
3601 if (phase == element_info[border_element].ignition_delay ||
3602 phase == last_phase)
3604 boolean border_explosion = FALSE;
3606 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3607 !PLAYER_EXPLOSION_PROTECTED(x, y))
3609 KillPlayerUnlessExplosionProtected(x, y);
3610 border_explosion = TRUE;
3612 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3614 Feld[x][y] = Store2[x][y];
3617 border_explosion = TRUE;
3619 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3621 AmoebeUmwandeln(x, y);
3623 border_explosion = TRUE;
3626 /* if an element just explodes due to another explosion (chain-reaction),
3627 do not immediately end the new explosion when it was the last frame of
3628 the explosion (as it would be done in the following "if"-statement!) */
3629 if (border_explosion && phase == last_phase)
3633 if (phase == last_phase)
3637 element = Feld[x][y] = Store[x][y];
3638 Store[x][y] = Store2[x][y] = 0;
3639 GfxElement[x][y] = EL_UNDEFINED;
3641 /* player can escape from explosions and might therefore be still alive */
3642 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3643 element <= EL_PLAYER_IS_EXPLODING_4)
3645 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3646 int explosion_element = EL_PLAYER_1 + player_nr;
3647 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3648 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3650 if (level.use_explosion_element[player_nr])
3651 explosion_element = level.explosion_element[player_nr];
3653 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3654 element_info[explosion_element].content.e[xx][yy]);
3657 /* restore probably existing indestructible background element */
3658 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3659 element = Feld[x][y] = Back[x][y];
3662 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3663 GfxDir[x][y] = MV_NONE;
3664 ChangeDelay[x][y] = 0;
3665 ChangePage[x][y] = -1;
3667 #if USE_NEW_CUSTOM_VALUE
3668 CustomValue[x][y] = 0;
3671 InitField_WithBug2(x, y, FALSE);
3673 DrawLevelField(x, y);
3675 TestIfElementTouchesCustomElement(x, y);
3677 if (GFX_CRUMBLED(element))
3678 DrawLevelFieldCrumbledSandNeighbours(x, y);
3680 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3681 StorePlayer[x][y] = 0;
3683 if (ELEM_IS_PLAYER(element))
3684 RelocatePlayer(x, y, element);
3686 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3688 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3689 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3692 DrawLevelFieldCrumbledSand(x, y);
3694 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3696 DrawLevelElement(x, y, Back[x][y]);
3697 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3699 else if (IS_WALKABLE_UNDER(Back[x][y]))
3701 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3702 DrawLevelElementThruMask(x, y, Back[x][y]);
3704 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3705 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3709 void DynaExplode(int ex, int ey)
3712 int dynabomb_element = Feld[ex][ey];
3713 int dynabomb_size = 1;
3714 boolean dynabomb_xl = FALSE;
3715 struct PlayerInfo *player;
3716 static int xy[4][2] =
3724 if (IS_ACTIVE_BOMB(dynabomb_element))
3726 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3727 dynabomb_size = player->dynabomb_size;
3728 dynabomb_xl = player->dynabomb_xl;
3729 player->dynabombs_left++;
3732 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3734 for (i = 0; i < NUM_DIRECTIONS; i++)
3736 for (j = 1; j <= dynabomb_size; j++)
3738 int x = ex + j * xy[i][0];
3739 int y = ey + j * xy[i][1];
3742 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3745 element = Feld[x][y];
3747 /* do not restart explosions of fields with active bombs */
3748 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3751 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3753 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3754 !IS_DIGGABLE(element) && !dynabomb_xl)
3760 void Bang(int x, int y)
3762 int element = MovingOrBlocked2Element(x, y);
3763 int explosion_type = EX_TYPE_NORMAL;
3765 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3767 struct PlayerInfo *player = PLAYERINFO(x, y);
3769 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3770 player->element_nr);
3772 if (level.use_explosion_element[player->index_nr])
3774 int explosion_element = level.explosion_element[player->index_nr];
3776 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3777 explosion_type = EX_TYPE_CROSS;
3778 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3779 explosion_type = EX_TYPE_CENTER;
3787 case EL_BD_BUTTERFLY:
3790 case EL_DARK_YAMYAM:
3794 RaiseScoreElement(element);
3797 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3798 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3799 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3800 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3801 case EL_DYNABOMB_INCREASE_NUMBER:
3802 case EL_DYNABOMB_INCREASE_SIZE:
3803 case EL_DYNABOMB_INCREASE_POWER:
3804 explosion_type = EX_TYPE_DYNA;
3809 case EL_LAMP_ACTIVE:
3810 case EL_AMOEBA_TO_DIAMOND:
3811 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3812 explosion_type = EX_TYPE_CENTER;
3816 if (element_info[element].explosion_type == EXPLODES_CROSS)
3817 explosion_type = EX_TYPE_CROSS;
3818 else if (element_info[element].explosion_type == EXPLODES_1X1)
3819 explosion_type = EX_TYPE_CENTER;
3823 if (explosion_type == EX_TYPE_DYNA)
3826 Explode(x, y, EX_PHASE_START, explosion_type);
3828 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3831 void SplashAcid(int x, int y)
3833 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3834 (!IN_LEV_FIELD(x - 1, y - 2) ||
3835 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3836 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3838 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3839 (!IN_LEV_FIELD(x + 1, y - 2) ||
3840 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3841 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3843 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3846 static void InitBeltMovement()
3848 static int belt_base_element[4] =
3850 EL_CONVEYOR_BELT_1_LEFT,
3851 EL_CONVEYOR_BELT_2_LEFT,
3852 EL_CONVEYOR_BELT_3_LEFT,
3853 EL_CONVEYOR_BELT_4_LEFT
3855 static int belt_base_active_element[4] =
3857 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3858 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3859 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3860 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3865 /* set frame order for belt animation graphic according to belt direction */
3866 for (i = 0; i < NUM_BELTS; i++)
3870 for (j = 0; j < NUM_BELT_PARTS; j++)
3872 int element = belt_base_active_element[belt_nr] + j;
3873 int graphic = el2img(element);
3875 if (game.belt_dir[i] == MV_LEFT)
3876 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3878 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3883 SCAN_PLAYFIELD(x, y)
3885 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3888 int element = Feld[x][y];
3890 for (i = 0; i < NUM_BELTS; i++)
3892 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3894 int e_belt_nr = getBeltNrFromBeltElement(element);
3897 if (e_belt_nr == belt_nr)
3899 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3901 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3908 static void ToggleBeltSwitch(int x, int y)
3910 static int belt_base_element[4] =
3912 EL_CONVEYOR_BELT_1_LEFT,
3913 EL_CONVEYOR_BELT_2_LEFT,
3914 EL_CONVEYOR_BELT_3_LEFT,
3915 EL_CONVEYOR_BELT_4_LEFT
3917 static int belt_base_active_element[4] =
3919 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3920 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3921 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3922 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3924 static int belt_base_switch_element[4] =
3926 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3927 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3928 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3929 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3931 static int belt_move_dir[4] =
3939 int element = Feld[x][y];
3940 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3941 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3942 int belt_dir = belt_move_dir[belt_dir_nr];
3945 if (!IS_BELT_SWITCH(element))
3948 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3949 game.belt_dir[belt_nr] = belt_dir;
3951 if (belt_dir_nr == 3)
3954 /* set frame order for belt animation graphic according to belt direction */
3955 for (i = 0; i < NUM_BELT_PARTS; i++)
3957 int element = belt_base_active_element[belt_nr] + i;
3958 int graphic = el2img(element);
3960 if (belt_dir == MV_LEFT)
3961 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3963 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3967 SCAN_PLAYFIELD(xx, yy)
3969 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3972 int element = Feld[xx][yy];
3974 if (IS_BELT_SWITCH(element))
3976 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3978 if (e_belt_nr == belt_nr)
3980 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3981 DrawLevelField(xx, yy);
3984 else if (IS_BELT(element) && belt_dir != MV_NONE)
3986 int e_belt_nr = getBeltNrFromBeltElement(element);
3988 if (e_belt_nr == belt_nr)
3990 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3992 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3993 DrawLevelField(xx, yy);
3996 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3998 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4000 if (e_belt_nr == belt_nr)
4002 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4004 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4005 DrawLevelField(xx, yy);
4011 static void ToggleSwitchgateSwitch(int x, int y)
4015 game.switchgate_pos = !game.switchgate_pos;
4018 SCAN_PLAYFIELD(xx, yy)
4020 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4023 int element = Feld[xx][yy];
4025 if (element == EL_SWITCHGATE_SWITCH_UP ||
4026 element == EL_SWITCHGATE_SWITCH_DOWN)
4028 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4029 DrawLevelField(xx, yy);
4031 else if (element == EL_SWITCHGATE_OPEN ||
4032 element == EL_SWITCHGATE_OPENING)
4034 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4036 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4038 else if (element == EL_SWITCHGATE_CLOSED ||
4039 element == EL_SWITCHGATE_CLOSING)
4041 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4043 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4048 static int getInvisibleActiveFromInvisibleElement(int element)
4050 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4051 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4052 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4056 static int getInvisibleFromInvisibleActiveElement(int element)
4058 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4059 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4060 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4064 static void RedrawAllLightSwitchesAndInvisibleElements()
4069 SCAN_PLAYFIELD(x, y)
4071 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4074 int element = Feld[x][y];
4076 if (element == EL_LIGHT_SWITCH &&
4077 game.light_time_left > 0)
4079 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4080 DrawLevelField(x, y);
4082 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4083 game.light_time_left == 0)
4085 Feld[x][y] = EL_LIGHT_SWITCH;
4086 DrawLevelField(x, y);
4088 else if (element == EL_EMC_DRIPPER &&
4089 game.light_time_left > 0)
4091 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4092 DrawLevelField(x, y);
4094 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4095 game.light_time_left == 0)
4097 Feld[x][y] = EL_EMC_DRIPPER;
4098 DrawLevelField(x, y);
4100 else if (element == EL_INVISIBLE_STEELWALL ||
4101 element == EL_INVISIBLE_WALL ||
4102 element == EL_INVISIBLE_SAND)
4104 if (game.light_time_left > 0)
4105 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4107 DrawLevelField(x, y);
4109 /* uncrumble neighbour fields, if needed */
4110 if (element == EL_INVISIBLE_SAND)
4111 DrawLevelFieldCrumbledSandNeighbours(x, y);
4113 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4114 element == EL_INVISIBLE_WALL_ACTIVE ||
4115 element == EL_INVISIBLE_SAND_ACTIVE)
4117 if (game.light_time_left == 0)
4118 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4120 DrawLevelField(x, y);
4122 /* re-crumble neighbour fields, if needed */
4123 if (element == EL_INVISIBLE_SAND)
4124 DrawLevelFieldCrumbledSandNeighbours(x, y);
4129 static void RedrawAllInvisibleElementsForLenses()
4134 SCAN_PLAYFIELD(x, y)
4136 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4139 int element = Feld[x][y];
4141 if (element == EL_EMC_DRIPPER &&
4142 game.lenses_time_left > 0)
4144 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4145 DrawLevelField(x, y);
4147 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4148 game.lenses_time_left == 0)
4150 Feld[x][y] = EL_EMC_DRIPPER;
4151 DrawLevelField(x, y);
4153 else if (element == EL_INVISIBLE_STEELWALL ||
4154 element == EL_INVISIBLE_WALL ||
4155 element == EL_INVISIBLE_SAND)
4157 if (game.lenses_time_left > 0)
4158 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4160 DrawLevelField(x, y);
4162 /* uncrumble neighbour fields, if needed */
4163 if (element == EL_INVISIBLE_SAND)
4164 DrawLevelFieldCrumbledSandNeighbours(x, y);
4166 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4167 element == EL_INVISIBLE_WALL_ACTIVE ||
4168 element == EL_INVISIBLE_SAND_ACTIVE)
4170 if (game.lenses_time_left == 0)
4171 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4173 DrawLevelField(x, y);
4175 /* re-crumble neighbour fields, if needed */
4176 if (element == EL_INVISIBLE_SAND)
4177 DrawLevelFieldCrumbledSandNeighbours(x, y);
4182 static void RedrawAllInvisibleElementsForMagnifier()
4187 SCAN_PLAYFIELD(x, y)
4189 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4192 int element = Feld[x][y];
4194 if (element == EL_EMC_FAKE_GRASS &&
4195 game.magnify_time_left > 0)
4197 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4198 DrawLevelField(x, y);
4200 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4201 game.magnify_time_left == 0)
4203 Feld[x][y] = EL_EMC_FAKE_GRASS;
4204 DrawLevelField(x, y);
4206 else if (IS_GATE_GRAY(element) &&
4207 game.magnify_time_left > 0)
4209 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4210 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4211 IS_EM_GATE_GRAY(element) ?
4212 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4213 IS_EMC_GATE_GRAY(element) ?
4214 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4216 DrawLevelField(x, y);
4218 else if (IS_GATE_GRAY_ACTIVE(element) &&
4219 game.magnify_time_left == 0)
4221 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4222 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4223 IS_EM_GATE_GRAY_ACTIVE(element) ?
4224 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4225 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4226 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4228 DrawLevelField(x, y);
4233 static void ToggleLightSwitch(int x, int y)
4235 int element = Feld[x][y];
4237 game.light_time_left =
4238 (element == EL_LIGHT_SWITCH ?
4239 level.time_light * FRAMES_PER_SECOND : 0);
4241 RedrawAllLightSwitchesAndInvisibleElements();
4244 static void ActivateTimegateSwitch(int x, int y)
4248 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4251 SCAN_PLAYFIELD(xx, yy)
4253 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4256 int element = Feld[xx][yy];
4258 if (element == EL_TIMEGATE_CLOSED ||
4259 element == EL_TIMEGATE_CLOSING)
4261 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4262 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4266 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4268 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4269 DrawLevelField(xx, yy);
4275 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4278 void Impact(int x, int y)
4280 boolean last_line = (y == lev_fieldy - 1);
4281 boolean object_hit = FALSE;
4282 boolean impact = (last_line || object_hit);
4283 int element = Feld[x][y];
4284 int smashed = EL_STEELWALL;
4286 if (!last_line) /* check if element below was hit */
4288 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4291 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4292 MovDir[x][y + 1] != MV_DOWN ||
4293 MovPos[x][y + 1] <= TILEY / 2));
4295 /* do not smash moving elements that left the smashed field in time */
4296 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4297 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4300 #if USE_QUICKSAND_IMPACT_BUGFIX
4301 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4303 RemoveMovingField(x, y + 1);
4304 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4305 Feld[x][y + 2] = EL_ROCK;
4306 DrawLevelField(x, y + 2);
4313 smashed = MovingOrBlocked2Element(x, y + 1);
4315 impact = (last_line || object_hit);
4318 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4320 SplashAcid(x, y + 1);
4324 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4325 /* only reset graphic animation if graphic really changes after impact */
4327 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4329 ResetGfxAnimation(x, y);
4330 DrawLevelField(x, y);
4333 if (impact && CAN_EXPLODE_IMPACT(element))
4338 else if (impact && element == EL_PEARL)
4340 ResetGfxAnimation(x, y);
4342 Feld[x][y] = EL_PEARL_BREAKING;
4343 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4346 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4348 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4353 if (impact && element == EL_AMOEBA_DROP)
4355 if (object_hit && IS_PLAYER(x, y + 1))
4356 KillPlayerUnlessEnemyProtected(x, y + 1);
4357 else if (object_hit && smashed == EL_PENGUIN)
4361 Feld[x][y] = EL_AMOEBA_GROWING;
4362 Store[x][y] = EL_AMOEBA_WET;
4364 ResetRandomAnimationValue(x, y);
4369 if (object_hit) /* check which object was hit */
4371 if (CAN_PASS_MAGIC_WALL(element) &&
4372 (smashed == EL_MAGIC_WALL ||
4373 smashed == EL_BD_MAGIC_WALL))
4376 int activated_magic_wall =
4377 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4378 EL_BD_MAGIC_WALL_ACTIVE);
4380 /* activate magic wall / mill */
4382 SCAN_PLAYFIELD(xx, yy)
4384 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4386 if (Feld[xx][yy] == smashed)
4387 Feld[xx][yy] = activated_magic_wall;
4389 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4390 game.magic_wall_active = TRUE;
4392 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4393 SND_MAGIC_WALL_ACTIVATING :
4394 SND_BD_MAGIC_WALL_ACTIVATING));
4397 if (IS_PLAYER(x, y + 1))
4399 if (CAN_SMASH_PLAYER(element))
4401 KillPlayerUnlessEnemyProtected(x, y + 1);
4405 else if (smashed == EL_PENGUIN)
4407 if (CAN_SMASH_PLAYER(element))
4413 else if (element == EL_BD_DIAMOND)
4415 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4421 else if (((element == EL_SP_INFOTRON ||
4422 element == EL_SP_ZONK) &&
4423 (smashed == EL_SP_SNIKSNAK ||
4424 smashed == EL_SP_ELECTRON ||
4425 smashed == EL_SP_DISK_ORANGE)) ||
4426 (element == EL_SP_INFOTRON &&
4427 smashed == EL_SP_DISK_YELLOW))
4432 else if (CAN_SMASH_EVERYTHING(element))
4434 if (IS_CLASSIC_ENEMY(smashed) ||
4435 CAN_EXPLODE_SMASHED(smashed))
4440 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4442 if (smashed == EL_LAMP ||
4443 smashed == EL_LAMP_ACTIVE)
4448 else if (smashed == EL_NUT)
4450 Feld[x][y + 1] = EL_NUT_BREAKING;
4451 PlayLevelSound(x, y, SND_NUT_BREAKING);
4452 RaiseScoreElement(EL_NUT);
4455 else if (smashed == EL_PEARL)
4457 ResetGfxAnimation(x, y);
4459 Feld[x][y + 1] = EL_PEARL_BREAKING;
4460 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4463 else if (smashed == EL_DIAMOND)
4465 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4466 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4469 else if (IS_BELT_SWITCH(smashed))
4471 ToggleBeltSwitch(x, y + 1);
4473 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4474 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4476 ToggleSwitchgateSwitch(x, y + 1);
4478 else if (smashed == EL_LIGHT_SWITCH ||
4479 smashed == EL_LIGHT_SWITCH_ACTIVE)
4481 ToggleLightSwitch(x, y + 1);
4486 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4489 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4491 CheckElementChangeBySide(x, y + 1, smashed, element,
4492 CE_SWITCHED, CH_SIDE_TOP);
4493 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4499 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4504 /* play sound of magic wall / mill */
4506 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4507 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4509 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4510 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4511 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4512 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4517 /* play sound of object that hits the ground */
4518 if (last_line || object_hit)
4519 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4522 inline static void TurnRoundExt(int x, int y)
4534 { 0, 0 }, { 0, 0 }, { 0, 0 },
4539 int left, right, back;
4543 { MV_DOWN, MV_UP, MV_RIGHT },
4544 { MV_UP, MV_DOWN, MV_LEFT },
4546 { MV_LEFT, MV_RIGHT, MV_DOWN },
4550 { MV_RIGHT, MV_LEFT, MV_UP }
4553 int element = Feld[x][y];
4554 int move_pattern = element_info[element].move_pattern;
4556 int old_move_dir = MovDir[x][y];
4557 int left_dir = turn[old_move_dir].left;
4558 int right_dir = turn[old_move_dir].right;
4559 int back_dir = turn[old_move_dir].back;
4561 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4562 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4563 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4564 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4566 int left_x = x + left_dx, left_y = y + left_dy;
4567 int right_x = x + right_dx, right_y = y + right_dy;
4568 int move_x = x + move_dx, move_y = y + move_dy;
4572 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4574 TestIfBadThingTouchesOtherBadThing(x, y);
4576 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4577 MovDir[x][y] = right_dir;
4578 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4579 MovDir[x][y] = left_dir;
4581 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4583 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4586 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4588 TestIfBadThingTouchesOtherBadThing(x, y);
4590 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4591 MovDir[x][y] = left_dir;
4592 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4593 MovDir[x][y] = right_dir;
4595 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4597 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4600 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4602 TestIfBadThingTouchesOtherBadThing(x, y);
4604 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4605 MovDir[x][y] = left_dir;
4606 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4607 MovDir[x][y] = right_dir;
4609 if (MovDir[x][y] != old_move_dir)
4612 else if (element == EL_YAMYAM)
4614 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4615 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4617 if (can_turn_left && can_turn_right)
4618 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4619 else if (can_turn_left)
4620 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4621 else if (can_turn_right)
4622 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4624 MovDir[x][y] = back_dir;
4626 MovDelay[x][y] = 16 + 16 * RND(3);
4628 else if (element == EL_DARK_YAMYAM)
4630 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4632 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4635 if (can_turn_left && can_turn_right)
4636 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4637 else if (can_turn_left)
4638 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4639 else if (can_turn_right)
4640 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4642 MovDir[x][y] = back_dir;
4644 MovDelay[x][y] = 16 + 16 * RND(3);
4646 else if (element == EL_PACMAN)
4648 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4649 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4651 if (can_turn_left && can_turn_right)
4652 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4653 else if (can_turn_left)
4654 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4655 else if (can_turn_right)
4656 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4658 MovDir[x][y] = back_dir;
4660 MovDelay[x][y] = 6 + RND(40);
4662 else if (element == EL_PIG)
4664 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4665 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4666 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4667 boolean should_turn_left, should_turn_right, should_move_on;
4669 int rnd = RND(rnd_value);
4671 should_turn_left = (can_turn_left &&
4673 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4674 y + back_dy + left_dy)));
4675 should_turn_right = (can_turn_right &&
4677 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4678 y + back_dy + right_dy)));
4679 should_move_on = (can_move_on &&
4682 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4683 y + move_dy + left_dy) ||
4684 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4685 y + move_dy + right_dy)));
4687 if (should_turn_left || should_turn_right || should_move_on)
4689 if (should_turn_left && should_turn_right && should_move_on)
4690 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4691 rnd < 2 * rnd_value / 3 ? right_dir :
4693 else if (should_turn_left && should_turn_right)
4694 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4695 else if (should_turn_left && should_move_on)
4696 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4697 else if (should_turn_right && should_move_on)
4698 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4699 else if (should_turn_left)
4700 MovDir[x][y] = left_dir;
4701 else if (should_turn_right)
4702 MovDir[x][y] = right_dir;
4703 else if (should_move_on)
4704 MovDir[x][y] = old_move_dir;
4706 else if (can_move_on && rnd > rnd_value / 8)
4707 MovDir[x][y] = old_move_dir;
4708 else if (can_turn_left && can_turn_right)
4709 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4710 else if (can_turn_left && rnd > rnd_value / 8)
4711 MovDir[x][y] = left_dir;
4712 else if (can_turn_right && rnd > rnd_value/8)
4713 MovDir[x][y] = right_dir;
4715 MovDir[x][y] = back_dir;
4717 xx = x + move_xy[MovDir[x][y]].dx;
4718 yy = y + move_xy[MovDir[x][y]].dy;
4720 if (!IN_LEV_FIELD(xx, yy) ||
4721 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4722 MovDir[x][y] = old_move_dir;
4726 else if (element == EL_DRAGON)
4728 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4729 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4730 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4732 int rnd = RND(rnd_value);
4734 if (can_move_on && rnd > rnd_value / 8)
4735 MovDir[x][y] = old_move_dir;
4736 else if (can_turn_left && can_turn_right)
4737 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4738 else if (can_turn_left && rnd > rnd_value / 8)
4739 MovDir[x][y] = left_dir;
4740 else if (can_turn_right && rnd > rnd_value / 8)
4741 MovDir[x][y] = right_dir;
4743 MovDir[x][y] = back_dir;
4745 xx = x + move_xy[MovDir[x][y]].dx;
4746 yy = y + move_xy[MovDir[x][y]].dy;
4748 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4749 MovDir[x][y] = old_move_dir;
4753 else if (element == EL_MOLE)
4755 boolean can_move_on =
4756 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4757 IS_AMOEBOID(Feld[move_x][move_y]) ||
4758 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4761 boolean can_turn_left =
4762 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4763 IS_AMOEBOID(Feld[left_x][left_y])));
4765 boolean can_turn_right =
4766 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4767 IS_AMOEBOID(Feld[right_x][right_y])));
4769 if (can_turn_left && can_turn_right)
4770 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4771 else if (can_turn_left)
4772 MovDir[x][y] = left_dir;
4774 MovDir[x][y] = right_dir;
4777 if (MovDir[x][y] != old_move_dir)
4780 else if (element == EL_BALLOON)
4782 MovDir[x][y] = game.wind_direction;
4785 else if (element == EL_SPRING)
4787 #if USE_NEW_SPRING_BUMPER
4788 if (MovDir[x][y] & MV_HORIZONTAL)
4790 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4791 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4793 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4794 ResetGfxAnimation(move_x, move_y);
4795 DrawLevelField(move_x, move_y);
4797 MovDir[x][y] = back_dir;
4799 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4800 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4801 MovDir[x][y] = MV_NONE;
4804 if (MovDir[x][y] & MV_HORIZONTAL &&
4805 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4806 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4807 MovDir[x][y] = MV_NONE;
4812 else if (element == EL_ROBOT ||
4813 element == EL_SATELLITE ||
4814 element == EL_PENGUIN ||
4815 element == EL_EMC_ANDROID)
4817 int attr_x = -1, attr_y = -1;
4828 for (i = 0; i < MAX_PLAYERS; i++)
4830 struct PlayerInfo *player = &stored_player[i];
4831 int jx = player->jx, jy = player->jy;
4833 if (!player->active)
4837 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4845 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4846 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4847 game.engine_version < VERSION_IDENT(3,1,0,0)))
4853 if (element == EL_PENGUIN)
4856 static int xy[4][2] =
4864 for (i = 0; i < NUM_DIRECTIONS; i++)
4866 int ex = x + xy[i][0];
4867 int ey = y + xy[i][1];
4869 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4878 MovDir[x][y] = MV_NONE;
4880 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4881 else if (attr_x > x)
4882 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4884 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4885 else if (attr_y > y)
4886 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4888 if (element == EL_ROBOT)
4892 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4893 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4894 Moving2Blocked(x, y, &newx, &newy);
4896 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4897 MovDelay[x][y] = 8 + 8 * !RND(3);
4899 MovDelay[x][y] = 16;
4901 else if (element == EL_PENGUIN)
4907 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4909 boolean first_horiz = RND(2);
4910 int new_move_dir = MovDir[x][y];
4913 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4914 Moving2Blocked(x, y, &newx, &newy);
4916 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4920 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4921 Moving2Blocked(x, y, &newx, &newy);
4923 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4926 MovDir[x][y] = old_move_dir;
4930 else if (element == EL_SATELLITE)
4936 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4938 boolean first_horiz = RND(2);
4939 int new_move_dir = MovDir[x][y];
4942 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4943 Moving2Blocked(x, y, &newx, &newy);
4945 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4949 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4950 Moving2Blocked(x, y, &newx, &newy);
4952 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4955 MovDir[x][y] = old_move_dir;
4959 else if (element == EL_EMC_ANDROID)
4961 static int check_pos[16] =
4963 -1, /* 0 => (invalid) */
4964 7, /* 1 => MV_LEFT */
4965 3, /* 2 => MV_RIGHT */
4966 -1, /* 3 => (invalid) */
4968 0, /* 5 => MV_LEFT | MV_UP */
4969 2, /* 6 => MV_RIGHT | MV_UP */
4970 -1, /* 7 => (invalid) */
4971 5, /* 8 => MV_DOWN */
4972 6, /* 9 => MV_LEFT | MV_DOWN */
4973 4, /* 10 => MV_RIGHT | MV_DOWN */
4974 -1, /* 11 => (invalid) */
4975 -1, /* 12 => (invalid) */
4976 -1, /* 13 => (invalid) */
4977 -1, /* 14 => (invalid) */
4978 -1, /* 15 => (invalid) */
4986 { -1, -1, MV_LEFT | MV_UP },
4988 { +1, -1, MV_RIGHT | MV_UP },
4989 { +1, 0, MV_RIGHT },
4990 { +1, +1, MV_RIGHT | MV_DOWN },
4992 { -1, +1, MV_LEFT | MV_DOWN },
4995 int start_pos, check_order;
4996 boolean can_clone = FALSE;
4999 /* check if there is any free field around current position */
5000 for (i = 0; i < 8; i++)
5002 int newx = x + check_xy[i].dx;
5003 int newy = y + check_xy[i].dy;
5005 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5013 if (can_clone) /* randomly find an element to clone */
5017 start_pos = check_pos[RND(8)];
5018 check_order = (RND(2) ? -1 : +1);
5020 for (i = 0; i < 8; i++)
5022 int pos_raw = start_pos + i * check_order;
5023 int pos = (pos_raw + 8) % 8;
5024 int newx = x + check_xy[pos].dx;
5025 int newy = y + check_xy[pos].dy;
5027 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5029 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5030 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5032 Store[x][y] = Feld[newx][newy];
5041 if (can_clone) /* randomly find a direction to move */
5045 start_pos = check_pos[RND(8)];
5046 check_order = (RND(2) ? -1 : +1);
5048 for (i = 0; i < 8; i++)
5050 int pos_raw = start_pos + i * check_order;
5051 int pos = (pos_raw + 8) % 8;
5052 int newx = x + check_xy[pos].dx;
5053 int newy = y + check_xy[pos].dy;
5054 int new_move_dir = check_xy[pos].dir;
5056 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5058 MovDir[x][y] = new_move_dir;
5059 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5068 if (can_clone) /* cloning and moving successful */
5071 /* cannot clone -- try to move towards player */
5073 start_pos = check_pos[MovDir[x][y] & 0x0f];
5074 check_order = (RND(2) ? -1 : +1);
5076 for (i = 0; i < 3; i++)
5078 /* first check start_pos, then previous/next or (next/previous) pos */
5079 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5080 int pos = (pos_raw + 8) % 8;
5081 int newx = x + check_xy[pos].dx;
5082 int newy = y + check_xy[pos].dy;
5083 int new_move_dir = check_xy[pos].dir;
5085 if (IS_PLAYER(newx, newy))
5088 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5090 MovDir[x][y] = new_move_dir;
5091 MovDelay[x][y] = level.android_move_time * 8 + 1;
5098 else if (move_pattern == MV_TURNING_LEFT ||
5099 move_pattern == MV_TURNING_RIGHT ||
5100 move_pattern == MV_TURNING_LEFT_RIGHT ||
5101 move_pattern == MV_TURNING_RIGHT_LEFT ||
5102 move_pattern == MV_TURNING_RANDOM ||
5103 move_pattern == MV_ALL_DIRECTIONS)
5105 boolean can_turn_left =
5106 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5107 boolean can_turn_right =
5108 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5110 if (element_info[element].move_stepsize == 0) /* "not moving" */
5113 if (move_pattern == MV_TURNING_LEFT)
5114 MovDir[x][y] = left_dir;
5115 else if (move_pattern == MV_TURNING_RIGHT)
5116 MovDir[x][y] = right_dir;
5117 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5118 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5119 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5120 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5121 else if (move_pattern == MV_TURNING_RANDOM)
5122 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5123 can_turn_right && !can_turn_left ? right_dir :
5124 RND(2) ? left_dir : right_dir);
5125 else if (can_turn_left && can_turn_right)
5126 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5127 else if (can_turn_left)
5128 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5129 else if (can_turn_right)
5130 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5132 MovDir[x][y] = back_dir;
5134 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5136 else if (move_pattern == MV_HORIZONTAL ||
5137 move_pattern == MV_VERTICAL)
5139 if (move_pattern & old_move_dir)
5140 MovDir[x][y] = back_dir;
5141 else if (move_pattern == MV_HORIZONTAL)
5142 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5143 else if (move_pattern == MV_VERTICAL)
5144 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5146 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5148 else if (move_pattern & MV_ANY_DIRECTION)
5150 MovDir[x][y] = move_pattern;
5151 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5153 else if (move_pattern & MV_WIND_DIRECTION)
5155 MovDir[x][y] = game.wind_direction;
5156 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5158 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5160 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5161 MovDir[x][y] = left_dir;
5162 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5163 MovDir[x][y] = right_dir;
5165 if (MovDir[x][y] != old_move_dir)
5166 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5168 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5170 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5171 MovDir[x][y] = right_dir;
5172 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5173 MovDir[x][y] = left_dir;
5175 if (MovDir[x][y] != old_move_dir)
5176 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5178 else if (move_pattern == MV_TOWARDS_PLAYER ||
5179 move_pattern == MV_AWAY_FROM_PLAYER)
5181 int attr_x = -1, attr_y = -1;
5183 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5194 for (i = 0; i < MAX_PLAYERS; i++)
5196 struct PlayerInfo *player = &stored_player[i];
5197 int jx = player->jx, jy = player->jy;
5199 if (!player->active)
5203 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5211 MovDir[x][y] = MV_NONE;
5213 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5214 else if (attr_x > x)
5215 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5217 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5218 else if (attr_y > y)
5219 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5221 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5223 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5225 boolean first_horiz = RND(2);
5226 int new_move_dir = MovDir[x][y];
5228 if (element_info[element].move_stepsize == 0) /* "not moving" */
5230 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5231 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5237 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5238 Moving2Blocked(x, y, &newx, &newy);
5240 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5244 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5245 Moving2Blocked(x, y, &newx, &newy);
5247 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5250 MovDir[x][y] = old_move_dir;
5253 else if (move_pattern == MV_WHEN_PUSHED ||
5254 move_pattern == MV_WHEN_DROPPED)
5256 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5257 MovDir[x][y] = MV_NONE;
5261 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5263 static int test_xy[7][2] =
5273 static int test_dir[7] =
5283 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5284 int move_preference = -1000000; /* start with very low preference */
5285 int new_move_dir = MV_NONE;
5286 int start_test = RND(4);
5289 for (i = 0; i < NUM_DIRECTIONS; i++)
5291 int move_dir = test_dir[start_test + i];
5292 int move_dir_preference;
5294 xx = x + test_xy[start_test + i][0];
5295 yy = y + test_xy[start_test + i][1];
5297 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5298 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5300 new_move_dir = move_dir;
5305 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5308 move_dir_preference = -1 * RunnerVisit[xx][yy];
5309 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5310 move_dir_preference = PlayerVisit[xx][yy];
5312 if (move_dir_preference > move_preference)
5314 /* prefer field that has not been visited for the longest time */
5315 move_preference = move_dir_preference;
5316 new_move_dir = move_dir;
5318 else if (move_dir_preference == move_preference &&
5319 move_dir == old_move_dir)
5321 /* prefer last direction when all directions are preferred equally */
5322 move_preference = move_dir_preference;
5323 new_move_dir = move_dir;
5327 MovDir[x][y] = new_move_dir;
5328 if (old_move_dir != new_move_dir)
5329 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5333 static void TurnRound(int x, int y)
5335 int direction = MovDir[x][y];
5337 int element, graphic;
5342 GfxDir[x][y] = MovDir[x][y];
5344 if (direction != MovDir[x][y])
5348 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5351 element = Feld[x][y];
5352 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5354 if (graphic_info[graphic].anim_global_sync)
5355 GfxFrame[x][y] = FrameCounter;
5356 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5357 GfxFrame[x][y] = CustomValue[x][y];
5358 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5359 GfxFrame[x][y] = element_info[element].collect_score;
5363 static boolean JustBeingPushed(int x, int y)
5367 for (i = 0; i < MAX_PLAYERS; i++)
5369 struct PlayerInfo *player = &stored_player[i];
5371 if (player->active && player->is_pushing && player->MovPos)
5373 int next_jx = player->jx + (player->jx - player->last_jx);
5374 int next_jy = player->jy + (player->jy - player->last_jy);
5376 if (x == next_jx && y == next_jy)
5384 void StartMoving(int x, int y)
5386 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5387 int element = Feld[x][y];
5392 if (MovDelay[x][y] == 0)
5393 GfxAction[x][y] = ACTION_DEFAULT;
5395 if (CAN_FALL(element) && y < lev_fieldy - 1)
5397 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5398 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5399 if (JustBeingPushed(x, y))
5402 if (element == EL_QUICKSAND_FULL)
5404 if (IS_FREE(x, y + 1))
5406 InitMovingField(x, y, MV_DOWN);
5407 started_moving = TRUE;
5409 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5410 Store[x][y] = EL_ROCK;
5412 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5414 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5416 if (!MovDelay[x][y])
5417 MovDelay[x][y] = TILEY + 1;
5426 Feld[x][y] = EL_QUICKSAND_EMPTY;
5427 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5428 Store[x][y + 1] = Store[x][y];
5431 PlayLevelSoundAction(x, y, ACTION_FILLING);
5434 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5435 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5437 InitMovingField(x, y, MV_DOWN);
5438 started_moving = TRUE;
5440 Feld[x][y] = EL_QUICKSAND_FILLING;
5441 Store[x][y] = element;
5443 PlayLevelSoundAction(x, y, ACTION_FILLING);
5445 else if (element == EL_MAGIC_WALL_FULL)
5447 if (IS_FREE(x, y + 1))
5449 InitMovingField(x, y, MV_DOWN);
5450 started_moving = TRUE;
5452 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5453 Store[x][y] = EL_CHANGED(Store[x][y]);
5455 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5457 if (!MovDelay[x][y])
5458 MovDelay[x][y] = TILEY/4 + 1;
5467 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5468 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5469 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5473 else if (element == EL_BD_MAGIC_WALL_FULL)
5475 if (IS_FREE(x, y + 1))
5477 InitMovingField(x, y, MV_DOWN);
5478 started_moving = TRUE;
5480 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5481 Store[x][y] = EL_CHANGED2(Store[x][y]);
5483 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5485 if (!MovDelay[x][y])
5486 MovDelay[x][y] = TILEY/4 + 1;
5495 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5496 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5497 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5501 else if (CAN_PASS_MAGIC_WALL(element) &&
5502 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5503 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5505 InitMovingField(x, y, MV_DOWN);
5506 started_moving = TRUE;
5509 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5510 EL_BD_MAGIC_WALL_FILLING);
5511 Store[x][y] = element;
5513 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5515 SplashAcid(x, y + 1);
5517 InitMovingField(x, y, MV_DOWN);
5518 started_moving = TRUE;
5520 Store[x][y] = EL_ACID;
5522 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5523 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5525 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5526 CAN_FALL(element) && WasJustFalling[x][y] &&
5527 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5529 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5530 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5531 (Feld[x][y + 1] == EL_BLOCKED)))
5533 /* this is needed for a special case not covered by calling "Impact()"
5534 from "ContinueMoving()": if an element moves to a tile directly below
5535 another element which was just falling on that tile (which was empty
5536 in the previous frame), the falling element above would just stop
5537 instead of smashing the element below (in previous version, the above
5538 element was just checked for "moving" instead of "falling", resulting
5539 in incorrect smashes caused by horizontal movement of the above
5540 element; also, the case of the player being the element to smash was
5541 simply not covered here... :-/ ) */
5543 CheckCollision[x][y] = 0;
5547 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5549 if (MovDir[x][y] == MV_NONE)
5551 InitMovingField(x, y, MV_DOWN);
5552 started_moving = TRUE;
5555 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5557 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5558 MovDir[x][y] = MV_DOWN;
5560 InitMovingField(x, y, MV_DOWN);
5561 started_moving = TRUE;
5563 else if (element == EL_AMOEBA_DROP)
5565 Feld[x][y] = EL_AMOEBA_GROWING;
5566 Store[x][y] = EL_AMOEBA_WET;
5568 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5569 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5570 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5571 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5573 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5574 (IS_FREE(x - 1, y + 1) ||
5575 Feld[x - 1][y + 1] == EL_ACID));
5576 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5577 (IS_FREE(x + 1, y + 1) ||
5578 Feld[x + 1][y + 1] == EL_ACID));
5579 boolean can_fall_any = (can_fall_left || can_fall_right);
5580 boolean can_fall_both = (can_fall_left && can_fall_right);
5581 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5583 #if USE_NEW_ALL_SLIPPERY
5584 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5586 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5587 can_fall_right = FALSE;
5588 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5589 can_fall_left = FALSE;
5590 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5591 can_fall_right = FALSE;
5592 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5593 can_fall_left = FALSE;
5595 can_fall_any = (can_fall_left || can_fall_right);
5596 can_fall_both = FALSE;
5599 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5601 if (slippery_type == SLIPPERY_ONLY_LEFT)
5602 can_fall_right = FALSE;
5603 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5604 can_fall_left = FALSE;
5605 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5606 can_fall_right = FALSE;
5607 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5608 can_fall_left = FALSE;
5610 can_fall_any = (can_fall_left || can_fall_right);
5611 can_fall_both = (can_fall_left && can_fall_right);
5615 #if USE_NEW_ALL_SLIPPERY
5617 #if USE_NEW_SP_SLIPPERY
5618 /* !!! better use the same properties as for custom elements here !!! */
5619 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5620 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5622 can_fall_right = FALSE; /* slip down on left side */
5623 can_fall_both = FALSE;
5628 #if USE_NEW_ALL_SLIPPERY
5631 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5632 can_fall_right = FALSE; /* slip down on left side */
5634 can_fall_left = !(can_fall_right = RND(2));
5636 can_fall_both = FALSE;
5641 if (game.emulation == EMU_BOULDERDASH ||
5642 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5643 can_fall_right = FALSE; /* slip down on left side */
5645 can_fall_left = !(can_fall_right = RND(2));
5647 can_fall_both = FALSE;
5653 /* if not determined otherwise, prefer left side for slipping down */
5654 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5655 started_moving = TRUE;
5659 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5661 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5664 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5665 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5666 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5667 int belt_dir = game.belt_dir[belt_nr];
5669 if ((belt_dir == MV_LEFT && left_is_free) ||
5670 (belt_dir == MV_RIGHT && right_is_free))
5672 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5674 InitMovingField(x, y, belt_dir);
5675 started_moving = TRUE;
5677 Pushed[x][y] = TRUE;
5678 Pushed[nextx][y] = TRUE;
5680 GfxAction[x][y] = ACTION_DEFAULT;
5684 MovDir[x][y] = 0; /* if element was moving, stop it */
5689 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5691 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5693 if (CAN_MOVE(element) && !started_moving)
5696 int move_pattern = element_info[element].move_pattern;
5701 if (MovDir[x][y] == MV_NONE)
5703 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5704 x, y, element, element_info[element].token_name);
5705 printf("StartMoving(): This should never happen!\n");
5710 Moving2Blocked(x, y, &newx, &newy);
5712 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5715 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5716 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5718 WasJustMoving[x][y] = 0;
5719 CheckCollision[x][y] = 0;
5721 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5723 if (Feld[x][y] != element) /* element has changed */
5727 if (!MovDelay[x][y]) /* start new movement phase */
5729 /* all objects that can change their move direction after each step
5730 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5732 if (element != EL_YAMYAM &&
5733 element != EL_DARK_YAMYAM &&
5734 element != EL_PACMAN &&
5735 !(move_pattern & MV_ANY_DIRECTION) &&
5736 move_pattern != MV_TURNING_LEFT &&
5737 move_pattern != MV_TURNING_RIGHT &&
5738 move_pattern != MV_TURNING_LEFT_RIGHT &&
5739 move_pattern != MV_TURNING_RIGHT_LEFT &&
5740 move_pattern != MV_TURNING_RANDOM)
5744 if (MovDelay[x][y] && (element == EL_BUG ||
5745 element == EL_SPACESHIP ||
5746 element == EL_SP_SNIKSNAK ||
5747 element == EL_SP_ELECTRON ||
5748 element == EL_MOLE))
5749 DrawLevelField(x, y);
5753 if (MovDelay[x][y]) /* wait some time before next movement */
5757 if (element == EL_ROBOT ||
5758 element == EL_YAMYAM ||
5759 element == EL_DARK_YAMYAM)
5761 DrawLevelElementAnimationIfNeeded(x, y, element);
5762 PlayLevelSoundAction(x, y, ACTION_WAITING);
5764 else if (element == EL_SP_ELECTRON)
5765 DrawLevelElementAnimationIfNeeded(x, y, element);
5766 else if (element == EL_DRAGON)
5769 int dir = MovDir[x][y];
5770 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5771 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5772 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5773 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5774 dir == MV_UP ? IMG_FLAMES_1_UP :
5775 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5776 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5778 GfxAction[x][y] = ACTION_ATTACKING;
5780 if (IS_PLAYER(x, y))
5781 DrawPlayerField(x, y);
5783 DrawLevelField(x, y);
5785 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5787 for (i = 1; i <= 3; i++)
5789 int xx = x + i * dx;
5790 int yy = y + i * dy;
5791 int sx = SCREENX(xx);
5792 int sy = SCREENY(yy);
5793 int flame_graphic = graphic + (i - 1);
5795 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5800 int flamed = MovingOrBlocked2Element(xx, yy);
5804 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5806 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5807 RemoveMovingField(xx, yy);
5809 RemoveField(xx, yy);
5811 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5814 RemoveMovingField(xx, yy);
5817 ChangeDelay[xx][yy] = 0;
5819 Feld[xx][yy] = EL_FLAMES;
5821 if (IN_SCR_FIELD(sx, sy))
5823 DrawLevelFieldCrumbledSand(xx, yy);
5824 DrawGraphic(sx, sy, flame_graphic, frame);
5829 if (Feld[xx][yy] == EL_FLAMES)
5830 Feld[xx][yy] = EL_EMPTY;
5831 DrawLevelField(xx, yy);
5836 if (MovDelay[x][y]) /* element still has to wait some time */
5838 PlayLevelSoundAction(x, y, ACTION_WAITING);
5844 /* now make next step */
5846 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5848 if (DONT_COLLIDE_WITH(element) &&
5849 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5850 !PLAYER_ENEMY_PROTECTED(newx, newy))
5852 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5857 else if (CAN_MOVE_INTO_ACID(element) &&
5858 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5859 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5860 (MovDir[x][y] == MV_DOWN ||
5861 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5863 SplashAcid(newx, newy);
5864 Store[x][y] = EL_ACID;
5866 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5868 if (Feld[newx][newy] == EL_EXIT_OPEN)
5871 DrawLevelField(x, y);
5873 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5874 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5875 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5877 local_player->friends_still_needed--;
5878 if (!local_player->friends_still_needed &&
5879 !local_player->GameOver && AllPlayersGone)
5880 local_player->LevelSolved = local_player->GameOver = TRUE;
5884 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5886 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5887 DrawLevelField(newx, newy);
5889 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5891 else if (!IS_FREE(newx, newy))
5893 GfxAction[x][y] = ACTION_WAITING;
5895 if (IS_PLAYER(x, y))
5896 DrawPlayerField(x, y);
5898 DrawLevelField(x, y);
5903 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5905 if (IS_FOOD_PIG(Feld[newx][newy]))
5907 if (IS_MOVING(newx, newy))
5908 RemoveMovingField(newx, newy);
5911 Feld[newx][newy] = EL_EMPTY;
5912 DrawLevelField(newx, newy);
5915 PlayLevelSound(x, y, SND_PIG_DIGGING);
5917 else if (!IS_FREE(newx, newy))
5919 if (IS_PLAYER(x, y))
5920 DrawPlayerField(x, y);
5922 DrawLevelField(x, y);
5927 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5929 if (Store[x][y] != EL_EMPTY)
5931 boolean can_clone = FALSE;
5934 /* check if element to clone is still there */
5935 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5937 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5945 /* cannot clone or target field not free anymore -- do not clone */
5946 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5947 Store[x][y] = EL_EMPTY;
5950 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5952 if (IS_MV_DIAGONAL(MovDir[x][y]))
5954 int diagonal_move_dir = MovDir[x][y];
5955 int stored = Store[x][y];
5956 int change_delay = 8;
5959 /* android is moving diagonally */
5961 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5963 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5964 GfxElement[x][y] = EL_EMC_ANDROID;
5965 GfxAction[x][y] = ACTION_SHRINKING;
5966 GfxDir[x][y] = diagonal_move_dir;
5967 ChangeDelay[x][y] = change_delay;
5969 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5972 DrawLevelGraphicAnimation(x, y, graphic);
5973 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5975 if (Feld[newx][newy] == EL_ACID)
5977 SplashAcid(newx, newy);
5982 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5984 Store[newx][newy] = EL_EMC_ANDROID;
5985 GfxElement[newx][newy] = EL_EMC_ANDROID;
5986 GfxAction[newx][newy] = ACTION_GROWING;
5987 GfxDir[newx][newy] = diagonal_move_dir;
5988 ChangeDelay[newx][newy] = change_delay;
5990 graphic = el_act_dir2img(GfxElement[newx][newy],
5991 GfxAction[newx][newy], GfxDir[newx][newy]);
5993 DrawLevelGraphicAnimation(newx, newy, graphic);
5994 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6000 Feld[newx][newy] = EL_EMPTY;
6001 DrawLevelField(newx, newy);
6003 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6006 else if (!IS_FREE(newx, newy))
6009 if (IS_PLAYER(x, y))
6010 DrawPlayerField(x, y);
6012 DrawLevelField(x, y);
6018 else if (IS_CUSTOM_ELEMENT(element) &&
6019 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6021 int new_element = Feld[newx][newy];
6023 if (!IS_FREE(newx, newy))
6025 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6026 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6029 /* no element can dig solid indestructible elements */
6030 if (IS_INDESTRUCTIBLE(new_element) &&
6031 !IS_DIGGABLE(new_element) &&
6032 !IS_COLLECTIBLE(new_element))
6035 if (AmoebaNr[newx][newy] &&
6036 (new_element == EL_AMOEBA_FULL ||
6037 new_element == EL_BD_AMOEBA ||
6038 new_element == EL_AMOEBA_GROWING))
6040 AmoebaCnt[AmoebaNr[newx][newy]]--;
6041 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6044 if (IS_MOVING(newx, newy))
6045 RemoveMovingField(newx, newy);
6048 RemoveField(newx, newy);
6049 DrawLevelField(newx, newy);
6052 /* if digged element was about to explode, prevent the explosion */
6053 ExplodeField[newx][newy] = EX_TYPE_NONE;
6055 PlayLevelSoundAction(x, y, action);
6058 Store[newx][newy] = EL_EMPTY;
6060 /* this makes it possible to leave the removed element again */
6061 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6062 Store[newx][newy] = new_element;
6064 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6066 int move_leave_element = element_info[element].move_leave_element;
6068 /* this makes it possible to leave the removed element again */
6069 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6070 new_element : move_leave_element);
6074 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6076 RunnerVisit[x][y] = FrameCounter;
6077 PlayerVisit[x][y] /= 8; /* expire player visit path */
6080 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6082 if (!IS_FREE(newx, newy))
6084 if (IS_PLAYER(x, y))
6085 DrawPlayerField(x, y);
6087 DrawLevelField(x, y);
6093 boolean wanna_flame = !RND(10);
6094 int dx = newx - x, dy = newy - y;
6095 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6096 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6097 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6098 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6099 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6100 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6103 IS_CLASSIC_ENEMY(element1) ||
6104 IS_CLASSIC_ENEMY(element2)) &&
6105 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6106 element1 != EL_FLAMES && element2 != EL_FLAMES)
6108 ResetGfxAnimation(x, y);
6109 GfxAction[x][y] = ACTION_ATTACKING;
6111 if (IS_PLAYER(x, y))
6112 DrawPlayerField(x, y);
6114 DrawLevelField(x, y);
6116 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6118 MovDelay[x][y] = 50;
6122 RemoveField(newx, newy);
6124 Feld[newx][newy] = EL_FLAMES;
6125 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6128 RemoveField(newx1, newy1);
6130 Feld[newx1][newy1] = EL_FLAMES;
6132 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6135 RemoveField(newx2, newy2);
6137 Feld[newx2][newy2] = EL_FLAMES;
6144 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6145 Feld[newx][newy] == EL_DIAMOND)
6147 if (IS_MOVING(newx, newy))
6148 RemoveMovingField(newx, newy);
6151 Feld[newx][newy] = EL_EMPTY;
6152 DrawLevelField(newx, newy);
6155 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6157 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6158 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6160 if (AmoebaNr[newx][newy])
6162 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6163 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6164 Feld[newx][newy] == EL_BD_AMOEBA)
6165 AmoebaCnt[AmoebaNr[newx][newy]]--;
6170 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6172 RemoveMovingField(newx, newy);
6175 if (IS_MOVING(newx, newy))
6177 RemoveMovingField(newx, newy);
6182 Feld[newx][newy] = EL_EMPTY;
6183 DrawLevelField(newx, newy);
6186 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6188 else if ((element == EL_PACMAN || element == EL_MOLE)
6189 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6191 if (AmoebaNr[newx][newy])
6193 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6194 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6195 Feld[newx][newy] == EL_BD_AMOEBA)
6196 AmoebaCnt[AmoebaNr[newx][newy]]--;
6199 if (element == EL_MOLE)
6201 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6202 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6204 ResetGfxAnimation(x, y);
6205 GfxAction[x][y] = ACTION_DIGGING;
6206 DrawLevelField(x, y);
6208 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6210 return; /* wait for shrinking amoeba */
6212 else /* element == EL_PACMAN */
6214 Feld[newx][newy] = EL_EMPTY;
6215 DrawLevelField(newx, newy);
6216 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6219 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6220 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6221 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6223 /* wait for shrinking amoeba to completely disappear */
6226 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6228 /* object was running against a wall */
6233 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6234 if (move_pattern & MV_ANY_DIRECTION &&
6235 move_pattern == MovDir[x][y])
6237 int blocking_element =
6238 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6240 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6243 element = Feld[x][y]; /* element might have changed */
6247 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6248 DrawLevelElementAnimation(x, y, element);
6250 if (DONT_TOUCH(element))
6251 TestIfBadThingTouchesPlayer(x, y);
6256 InitMovingField(x, y, MovDir[x][y]);
6258 PlayLevelSoundAction(x, y, ACTION_MOVING);
6262 ContinueMoving(x, y);
6265 void ContinueMoving(int x, int y)
6267 int element = Feld[x][y];
6268 struct ElementInfo *ei = &element_info[element];
6269 int direction = MovDir[x][y];
6270 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6271 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6272 int newx = x + dx, newy = y + dy;
6273 int stored = Store[x][y];
6274 int stored_new = Store[newx][newy];
6275 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6276 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6277 boolean last_line = (newy == lev_fieldy - 1);
6279 MovPos[x][y] += getElementMoveStepsize(x, y);
6281 if (pushed_by_player) /* special case: moving object pushed by player */
6282 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6284 if (ABS(MovPos[x][y]) < TILEX)
6286 DrawLevelField(x, y);
6288 return; /* element is still moving */
6291 /* element reached destination field */
6293 Feld[x][y] = EL_EMPTY;
6294 Feld[newx][newy] = element;
6295 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6297 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6299 element = Feld[newx][newy] = EL_ACID;
6301 else if (element == EL_MOLE)
6303 Feld[x][y] = EL_SAND;
6305 DrawLevelFieldCrumbledSandNeighbours(x, y);
6307 else if (element == EL_QUICKSAND_FILLING)
6309 element = Feld[newx][newy] = get_next_element(element);
6310 Store[newx][newy] = Store[x][y];
6312 else if (element == EL_QUICKSAND_EMPTYING)
6314 Feld[x][y] = get_next_element(element);
6315 element = Feld[newx][newy] = Store[x][y];
6317 else if (element == EL_MAGIC_WALL_FILLING)
6319 element = Feld[newx][newy] = get_next_element(element);
6320 if (!game.magic_wall_active)
6321 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6322 Store[newx][newy] = Store[x][y];
6324 else if (element == EL_MAGIC_WALL_EMPTYING)
6326 Feld[x][y] = get_next_element(element);
6327 if (!game.magic_wall_active)
6328 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6329 element = Feld[newx][newy] = Store[x][y];
6331 #if USE_NEW_CUSTOM_VALUE
6332 InitField(newx, newy, FALSE);
6335 else if (element == EL_BD_MAGIC_WALL_FILLING)
6337 element = Feld[newx][newy] = get_next_element(element);
6338 if (!game.magic_wall_active)
6339 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6340 Store[newx][newy] = Store[x][y];
6342 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6344 Feld[x][y] = get_next_element(element);
6345 if (!game.magic_wall_active)
6346 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6347 element = Feld[newx][newy] = Store[x][y];
6349 #if USE_NEW_CUSTOM_VALUE
6350 InitField(newx, newy, FALSE);
6353 else if (element == EL_AMOEBA_DROPPING)
6355 Feld[x][y] = get_next_element(element);
6356 element = Feld[newx][newy] = Store[x][y];
6358 else if (element == EL_SOKOBAN_OBJECT)
6361 Feld[x][y] = Back[x][y];
6363 if (Back[newx][newy])
6364 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6366 Back[x][y] = Back[newx][newy] = 0;
6369 Store[x][y] = EL_EMPTY;
6374 MovDelay[newx][newy] = 0;
6377 if (CAN_CHANGE_OR_HAS_ACTION(element))
6379 if (CAN_CHANGE(element))
6382 /* copy element change control values to new field */
6383 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6384 ChangePage[newx][newy] = ChangePage[x][y];
6385 ChangeCount[newx][newy] = ChangeCount[x][y];
6386 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6389 #if USE_NEW_CUSTOM_VALUE
6390 CustomValue[newx][newy] = CustomValue[x][y];
6396 #if USE_NEW_CUSTOM_VALUE
6397 CustomValue[newx][newy] = CustomValue[x][y];
6401 ChangeDelay[x][y] = 0;
6402 ChangePage[x][y] = -1;
6403 ChangeCount[x][y] = 0;
6404 ChangeEvent[x][y] = -1;
6406 #if USE_NEW_CUSTOM_VALUE
6407 CustomValue[x][y] = 0;
6410 /* copy animation control values to new field */
6411 GfxFrame[newx][newy] = GfxFrame[x][y];
6412 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6413 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6414 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6416 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6418 /* some elements can leave other elements behind after moving */
6420 if (ei->move_leave_element != EL_EMPTY &&
6421 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6422 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6424 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6425 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6426 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6429 int move_leave_element = ei->move_leave_element;
6433 /* this makes it possible to leave the removed element again */
6434 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6435 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6437 /* this makes it possible to leave the removed element again */
6438 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6439 move_leave_element = stored;
6442 /* this makes it possible to leave the removed element again */
6443 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6444 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6445 move_leave_element = stored;
6448 Feld[x][y] = move_leave_element;
6450 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6451 MovDir[x][y] = direction;
6453 InitField(x, y, FALSE);
6455 if (GFX_CRUMBLED(Feld[x][y]))
6456 DrawLevelFieldCrumbledSandNeighbours(x, y);
6458 if (ELEM_IS_PLAYER(move_leave_element))
6459 RelocatePlayer(x, y, move_leave_element);
6462 /* do this after checking for left-behind element */
6463 ResetGfxAnimation(x, y); /* reset animation values for old field */
6465 if (!CAN_MOVE(element) ||
6466 (CAN_FALL(element) && direction == MV_DOWN &&
6467 (element == EL_SPRING ||
6468 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6469 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6470 GfxDir[x][y] = MovDir[newx][newy] = 0;
6472 DrawLevelField(x, y);
6473 DrawLevelField(newx, newy);
6475 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6477 /* prevent pushed element from moving on in pushed direction */
6478 if (pushed_by_player && CAN_MOVE(element) &&
6479 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6480 !(element_info[element].move_pattern & direction))
6481 TurnRound(newx, newy);
6483 /* prevent elements on conveyor belt from moving on in last direction */
6484 if (pushed_by_conveyor && CAN_FALL(element) &&
6485 direction & MV_HORIZONTAL)
6486 MovDir[newx][newy] = 0;
6488 if (!pushed_by_player)
6490 int nextx = newx + dx, nexty = newy + dy;
6491 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6493 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6495 if (CAN_FALL(element) && direction == MV_DOWN)
6496 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6498 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6499 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6502 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6504 TestIfBadThingTouchesPlayer(newx, newy);
6505 TestIfBadThingTouchesFriend(newx, newy);
6507 if (!IS_CUSTOM_ELEMENT(element))
6508 TestIfBadThingTouchesOtherBadThing(newx, newy);
6510 else if (element == EL_PENGUIN)
6511 TestIfFriendTouchesBadThing(newx, newy);
6513 /* give the player one last chance (one more frame) to move away */
6514 if (CAN_FALL(element) && direction == MV_DOWN &&
6515 (last_line || (!IS_FREE(x, newy + 1) &&
6516 (!IS_PLAYER(x, newy + 1) ||
6517 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6520 if (pushed_by_player && !game.use_change_when_pushing_bug)
6522 int push_side = MV_DIR_OPPOSITE(direction);
6523 struct PlayerInfo *player = PLAYERINFO(x, y);
6525 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6526 player->index_bit, push_side);
6527 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6528 player->index_bit, push_side);
6531 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6532 MovDelay[newx][newy] = 1;
6534 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6536 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6539 if (ChangePage[newx][newy] != -1) /* delayed change */
6541 int page = ChangePage[newx][newy];
6542 struct ElementChangeInfo *change = &ei->change_page[page];
6544 ChangePage[newx][newy] = -1;
6546 if (change->can_change)
6548 if (ChangeElement(newx, newy, element, page))
6550 if (change->post_change_function)
6551 change->post_change_function(newx, newy);
6555 if (change->has_action)
6556 ExecuteCustomElementAction(newx, newy, element, page);
6560 TestIfElementHitsCustomElement(newx, newy, direction);
6561 TestIfPlayerTouchesCustomElement(newx, newy);
6562 TestIfElementTouchesCustomElement(newx, newy);
6565 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6566 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6567 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6568 MV_DIR_OPPOSITE(direction));
6572 int AmoebeNachbarNr(int ax, int ay)
6575 int element = Feld[ax][ay];
6577 static int xy[4][2] =
6585 for (i = 0; i < NUM_DIRECTIONS; i++)
6587 int x = ax + xy[i][0];
6588 int y = ay + xy[i][1];
6590 if (!IN_LEV_FIELD(x, y))
6593 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6594 group_nr = AmoebaNr[x][y];
6600 void AmoebenVereinigen(int ax, int ay)
6602 int i, x, y, xx, yy;
6603 int new_group_nr = AmoebaNr[ax][ay];
6604 static int xy[4][2] =
6612 if (new_group_nr == 0)
6615 for (i = 0; i < NUM_DIRECTIONS; i++)
6620 if (!IN_LEV_FIELD(x, y))
6623 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6624 Feld[x][y] == EL_BD_AMOEBA ||
6625 Feld[x][y] == EL_AMOEBA_DEAD) &&
6626 AmoebaNr[x][y] != new_group_nr)
6628 int old_group_nr = AmoebaNr[x][y];
6630 if (old_group_nr == 0)
6633 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6634 AmoebaCnt[old_group_nr] = 0;
6635 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6636 AmoebaCnt2[old_group_nr] = 0;
6639 SCAN_PLAYFIELD(xx, yy)
6641 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6644 if (AmoebaNr[xx][yy] == old_group_nr)
6645 AmoebaNr[xx][yy] = new_group_nr;
6651 void AmoebeUmwandeln(int ax, int ay)
6655 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6657 int group_nr = AmoebaNr[ax][ay];
6662 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6663 printf("AmoebeUmwandeln(): This should never happen!\n");
6669 SCAN_PLAYFIELD(x, y)
6671 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6674 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6677 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6681 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6682 SND_AMOEBA_TURNING_TO_GEM :
6683 SND_AMOEBA_TURNING_TO_ROCK));
6688 static int xy[4][2] =
6696 for (i = 0; i < NUM_DIRECTIONS; i++)
6701 if (!IN_LEV_FIELD(x, y))
6704 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6706 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6707 SND_AMOEBA_TURNING_TO_GEM :
6708 SND_AMOEBA_TURNING_TO_ROCK));
6715 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6718 int group_nr = AmoebaNr[ax][ay];
6719 boolean done = FALSE;
6724 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6725 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6731 SCAN_PLAYFIELD(x, y)
6733 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6736 if (AmoebaNr[x][y] == group_nr &&
6737 (Feld[x][y] == EL_AMOEBA_DEAD ||
6738 Feld[x][y] == EL_BD_AMOEBA ||
6739 Feld[x][y] == EL_AMOEBA_GROWING))
6742 Feld[x][y] = new_element;
6743 InitField(x, y, FALSE);
6744 DrawLevelField(x, y);
6750 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6751 SND_BD_AMOEBA_TURNING_TO_ROCK :
6752 SND_BD_AMOEBA_TURNING_TO_GEM));
6755 void AmoebeWaechst(int x, int y)
6757 static unsigned long sound_delay = 0;
6758 static unsigned long sound_delay_value = 0;
6760 if (!MovDelay[x][y]) /* start new growing cycle */
6764 if (DelayReached(&sound_delay, sound_delay_value))
6766 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6767 sound_delay_value = 30;
6771 if (MovDelay[x][y]) /* wait some time before growing bigger */
6774 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6776 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6777 6 - MovDelay[x][y]);
6779 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6782 if (!MovDelay[x][y])
6784 Feld[x][y] = Store[x][y];
6786 DrawLevelField(x, y);
6791 void AmoebaDisappearing(int x, int y)
6793 static unsigned long sound_delay = 0;
6794 static unsigned long sound_delay_value = 0;
6796 if (!MovDelay[x][y]) /* start new shrinking cycle */
6800 if (DelayReached(&sound_delay, sound_delay_value))
6801 sound_delay_value = 30;
6804 if (MovDelay[x][y]) /* wait some time before shrinking */
6807 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6809 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6810 6 - MovDelay[x][y]);
6812 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6815 if (!MovDelay[x][y])
6817 Feld[x][y] = EL_EMPTY;
6818 DrawLevelField(x, y);
6820 /* don't let mole enter this field in this cycle;
6821 (give priority to objects falling to this field from above) */
6827 void AmoebeAbleger(int ax, int ay)
6830 int element = Feld[ax][ay];
6831 int graphic = el2img(element);
6832 int newax = ax, neway = ay;
6833 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6834 static int xy[4][2] =
6842 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6844 Feld[ax][ay] = EL_AMOEBA_DEAD;
6845 DrawLevelField(ax, ay);
6849 if (IS_ANIMATED(graphic))
6850 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6852 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6853 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6855 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6858 if (MovDelay[ax][ay])
6862 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6865 int x = ax + xy[start][0];
6866 int y = ay + xy[start][1];
6868 if (!IN_LEV_FIELD(x, y))
6871 if (IS_FREE(x, y) ||
6872 CAN_GROW_INTO(Feld[x][y]) ||
6873 Feld[x][y] == EL_QUICKSAND_EMPTY)
6879 if (newax == ax && neway == ay)
6882 else /* normal or "filled" (BD style) amoeba */
6885 boolean waiting_for_player = FALSE;
6887 for (i = 0; i < NUM_DIRECTIONS; i++)
6889 int j = (start + i) % 4;
6890 int x = ax + xy[j][0];
6891 int y = ay + xy[j][1];
6893 if (!IN_LEV_FIELD(x, y))
6896 if (IS_FREE(x, y) ||
6897 CAN_GROW_INTO(Feld[x][y]) ||
6898 Feld[x][y] == EL_QUICKSAND_EMPTY)
6904 else if (IS_PLAYER(x, y))
6905 waiting_for_player = TRUE;
6908 if (newax == ax && neway == ay) /* amoeba cannot grow */
6910 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6912 Feld[ax][ay] = EL_AMOEBA_DEAD;
6913 DrawLevelField(ax, ay);
6914 AmoebaCnt[AmoebaNr[ax][ay]]--;
6916 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6918 if (element == EL_AMOEBA_FULL)
6919 AmoebeUmwandeln(ax, ay);
6920 else if (element == EL_BD_AMOEBA)
6921 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6926 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6928 /* amoeba gets larger by growing in some direction */
6930 int new_group_nr = AmoebaNr[ax][ay];
6933 if (new_group_nr == 0)
6935 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6936 printf("AmoebeAbleger(): This should never happen!\n");
6941 AmoebaNr[newax][neway] = new_group_nr;
6942 AmoebaCnt[new_group_nr]++;
6943 AmoebaCnt2[new_group_nr]++;
6945 /* if amoeba touches other amoeba(s) after growing, unify them */
6946 AmoebenVereinigen(newax, neway);
6948 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6950 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6956 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6957 (neway == lev_fieldy - 1 && newax != ax))
6959 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6960 Store[newax][neway] = element;
6962 else if (neway == ay || element == EL_EMC_DRIPPER)
6964 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6966 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6970 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6971 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6972 Store[ax][ay] = EL_AMOEBA_DROP;
6973 ContinueMoving(ax, ay);
6977 DrawLevelField(newax, neway);
6980 void Life(int ax, int ay)
6984 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6987 int element = Feld[ax][ay];
6988 int graphic = el2img(element);
6989 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6991 boolean changed = FALSE;
6993 if (IS_ANIMATED(graphic))
6994 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6999 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7000 MovDelay[ax][ay] = life_time;
7002 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7005 if (MovDelay[ax][ay])
7009 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7011 int xx = ax+x1, yy = ay+y1;
7014 if (!IN_LEV_FIELD(xx, yy))
7017 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7019 int x = xx+x2, y = yy+y2;
7021 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7024 if (((Feld[x][y] == element ||
7025 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7027 (IS_FREE(x, y) && Stop[x][y]))
7031 if (xx == ax && yy == ay) /* field in the middle */
7033 if (nachbarn < life_parameter[0] ||
7034 nachbarn > life_parameter[1])
7036 Feld[xx][yy] = EL_EMPTY;
7038 DrawLevelField(xx, yy);
7039 Stop[xx][yy] = TRUE;
7043 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7044 { /* free border field */
7045 if (nachbarn >= life_parameter[2] &&
7046 nachbarn <= life_parameter[3])
7048 Feld[xx][yy] = element;
7049 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7051 DrawLevelField(xx, yy);
7052 Stop[xx][yy] = TRUE;
7059 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7060 SND_GAME_OF_LIFE_GROWING);
7063 static void InitRobotWheel(int x, int y)
7065 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7068 static void RunRobotWheel(int x, int y)
7070 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7073 static void StopRobotWheel(int x, int y)
7075 if (ZX == x && ZY == y)
7079 static void InitTimegateWheel(int x, int y)
7081 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7084 static void RunTimegateWheel(int x, int y)
7086 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7089 static void InitMagicBallDelay(int x, int y)
7092 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7094 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7098 static void ActivateMagicBall(int bx, int by)
7102 if (level.ball_random)
7104 int pos_border = RND(8); /* select one of the eight border elements */
7105 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7106 int xx = pos_content % 3;
7107 int yy = pos_content / 3;
7112 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7113 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7117 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7119 int xx = x - bx + 1;
7120 int yy = y - by + 1;
7122 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7123 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7127 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7130 static void InitDiagonalMovingElement(int x, int y)
7133 MovDelay[x][y] = level.android_move_time;
7137 void CheckExit(int x, int y)
7139 if (local_player->gems_still_needed > 0 ||
7140 local_player->sokobanfields_still_needed > 0 ||
7141 local_player->lights_still_needed > 0)
7143 int element = Feld[x][y];
7144 int graphic = el2img(element);
7146 if (IS_ANIMATED(graphic))
7147 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7152 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7155 Feld[x][y] = EL_EXIT_OPENING;
7157 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7160 void CheckExitSP(int x, int y)
7162 if (local_player->gems_still_needed > 0)
7164 int element = Feld[x][y];
7165 int graphic = el2img(element);
7167 if (IS_ANIMATED(graphic))
7168 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7173 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7176 Feld[x][y] = EL_SP_EXIT_OPENING;
7178 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7181 static void CloseAllOpenTimegates()
7186 SCAN_PLAYFIELD(x, y)
7188 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7191 int element = Feld[x][y];
7193 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7195 Feld[x][y] = EL_TIMEGATE_CLOSING;
7197 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7202 void EdelsteinFunkeln(int x, int y)
7204 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7207 if (Feld[x][y] == EL_BD_DIAMOND)
7210 if (MovDelay[x][y] == 0) /* next animation frame */
7211 MovDelay[x][y] = 11 * !SimpleRND(500);
7213 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7217 if (setup.direct_draw && MovDelay[x][y])
7218 SetDrawtoField(DRAW_BUFFERED);
7220 DrawLevelElementAnimation(x, y, Feld[x][y]);
7222 if (MovDelay[x][y] != 0)
7224 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7225 10 - MovDelay[x][y]);
7227 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7229 if (setup.direct_draw)
7233 dest_x = FX + SCREENX(x) * TILEX;
7234 dest_y = FY + SCREENY(y) * TILEY;
7236 BlitBitmap(drawto_field, window,
7237 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7238 SetDrawtoField(DRAW_DIRECT);
7244 void MauerWaechst(int x, int y)
7248 if (!MovDelay[x][y]) /* next animation frame */
7249 MovDelay[x][y] = 3 * delay;
7251 if (MovDelay[x][y]) /* wait some time before next frame */
7255 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7257 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7258 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7260 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7263 if (!MovDelay[x][y])
7265 if (MovDir[x][y] == MV_LEFT)
7267 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7268 DrawLevelField(x - 1, y);
7270 else if (MovDir[x][y] == MV_RIGHT)
7272 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7273 DrawLevelField(x + 1, y);
7275 else if (MovDir[x][y] == MV_UP)
7277 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7278 DrawLevelField(x, y - 1);
7282 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7283 DrawLevelField(x, y + 1);
7286 Feld[x][y] = Store[x][y];
7288 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7289 DrawLevelField(x, y);
7294 void MauerAbleger(int ax, int ay)
7296 int element = Feld[ax][ay];
7297 int graphic = el2img(element);
7298 boolean oben_frei = FALSE, unten_frei = FALSE;
7299 boolean links_frei = FALSE, rechts_frei = FALSE;
7300 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7301 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7302 boolean new_wall = FALSE;
7304 if (IS_ANIMATED(graphic))
7305 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7307 if (!MovDelay[ax][ay]) /* start building new wall */
7308 MovDelay[ax][ay] = 6;
7310 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7313 if (MovDelay[ax][ay])
7317 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7319 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7321 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7323 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7326 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7327 element == EL_EXPANDABLE_WALL_ANY)
7331 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7332 Store[ax][ay-1] = element;
7333 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7334 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7335 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7336 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7341 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7342 Store[ax][ay+1] = element;
7343 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7344 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7345 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7346 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7351 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7352 element == EL_EXPANDABLE_WALL_ANY ||
7353 element == EL_EXPANDABLE_WALL)
7357 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7358 Store[ax-1][ay] = element;
7359 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7360 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7361 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7362 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7368 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7369 Store[ax+1][ay] = element;
7370 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7371 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7372 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7373 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7378 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7379 DrawLevelField(ax, ay);
7381 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7383 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7384 unten_massiv = TRUE;
7385 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7386 links_massiv = TRUE;
7387 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7388 rechts_massiv = TRUE;
7390 if (((oben_massiv && unten_massiv) ||
7391 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7392 element == EL_EXPANDABLE_WALL) &&
7393 ((links_massiv && rechts_massiv) ||
7394 element == EL_EXPANDABLE_WALL_VERTICAL))
7395 Feld[ax][ay] = EL_WALL;
7398 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7401 void CheckForDragon(int x, int y)
7404 boolean dragon_found = FALSE;
7405 static int xy[4][2] =
7413 for (i = 0; i < NUM_DIRECTIONS; i++)
7415 for (j = 0; j < 4; j++)
7417 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7419 if (IN_LEV_FIELD(xx, yy) &&
7420 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7422 if (Feld[xx][yy] == EL_DRAGON)
7423 dragon_found = TRUE;
7432 for (i = 0; i < NUM_DIRECTIONS; i++)
7434 for (j = 0; j < 3; j++)
7436 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7438 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7440 Feld[xx][yy] = EL_EMPTY;
7441 DrawLevelField(xx, yy);
7450 static void InitBuggyBase(int x, int y)
7452 int element = Feld[x][y];
7453 int activating_delay = FRAMES_PER_SECOND / 4;
7456 (element == EL_SP_BUGGY_BASE ?
7457 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7458 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7460 element == EL_SP_BUGGY_BASE_ACTIVE ?
7461 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7464 static void WarnBuggyBase(int x, int y)
7467 static int xy[4][2] =
7475 for (i = 0; i < NUM_DIRECTIONS; i++)
7477 int xx = x + xy[i][0];
7478 int yy = y + xy[i][1];
7480 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7482 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7489 static void InitTrap(int x, int y)
7491 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7494 static void ActivateTrap(int x, int y)
7496 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7499 static void ChangeActiveTrap(int x, int y)
7501 int graphic = IMG_TRAP_ACTIVE;
7503 /* if new animation frame was drawn, correct crumbled sand border */
7504 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7505 DrawLevelFieldCrumbledSand(x, y);
7508 static int getSpecialActionElement(int element, int number, int base_element)
7510 return (element != EL_EMPTY ? element :
7511 number != -1 ? base_element + number - 1 :
7515 static int getModifiedActionNumber(int value_old, int operator, int operand,
7516 int value_min, int value_max)
7518 int value_new = (operator == CA_MODE_SET ? operand :
7519 operator == CA_MODE_ADD ? value_old + operand :
7520 operator == CA_MODE_SUBTRACT ? value_old - operand :
7521 operator == CA_MODE_MULTIPLY ? value_old * operand :
7522 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7523 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7526 return (value_new < value_min ? value_min :
7527 value_new > value_max ? value_max :
7531 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7533 struct ElementInfo *ei = &element_info[element];
7534 struct ElementChangeInfo *change = &ei->change_page[page];
7535 int action_type = change->action_type;
7536 int action_mode = change->action_mode;
7537 int action_arg = change->action_arg;
7540 if (!change->has_action)
7543 /* ---------- determine action paramater values -------------------------- */
7545 int level_time_value =
7546 (level.time > 0 ? TimeLeft :
7549 int action_arg_element =
7550 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7551 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7552 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7555 int action_arg_direction =
7556 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7557 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7558 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7559 change->actual_trigger_side :
7560 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7561 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7564 int action_arg_number_min =
7565 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7568 int action_arg_number_max =
7569 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7570 action_type == CA_SET_LEVEL_GEMS ? 999 :
7571 action_type == CA_SET_LEVEL_TIME ? 9999 :
7572 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7573 action_type == CA_SET_CE_SCORE ? 9999 :
7574 action_type == CA_SET_CE_VALUE ? 9999 :
7577 int action_arg_number_reset =
7578 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7579 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7580 action_type == CA_SET_LEVEL_TIME ? level.time :
7581 action_type == CA_SET_LEVEL_SCORE ? 0 :
7582 action_type == CA_SET_CE_SCORE ? 0 :
7584 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7586 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7590 int action_arg_number =
7591 (action_arg <= CA_ARG_MAX ? action_arg :
7592 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7593 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7594 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7595 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7596 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7597 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7598 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7599 #if USE_NEW_CUSTOM_VALUE
7600 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7602 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7604 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7605 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7606 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7607 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7608 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7609 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7610 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7611 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7614 int action_arg_number_old =
7615 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7616 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7617 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7618 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7619 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7622 int action_arg_number_new =
7623 getModifiedActionNumber(action_arg_number_old,
7624 action_mode, action_arg_number,
7625 action_arg_number_min, action_arg_number_max);
7627 int trigger_player_bits =
7628 (change->actual_trigger_player >= EL_PLAYER_1 &&
7629 change->actual_trigger_player <= EL_PLAYER_4 ?
7630 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7633 int action_arg_player_bits =
7634 (action_arg >= CA_ARG_PLAYER_1 &&
7635 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7636 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7639 /* ---------- execute action -------------------------------------------- */
7648 /* ---------- level actions ------------------------------------------- */
7650 case CA_RESTART_LEVEL:
7652 game.restart_level = TRUE;
7657 case CA_SHOW_ENVELOPE:
7659 int element = getSpecialActionElement(action_arg_element,
7660 action_arg_number, EL_ENVELOPE_1);
7662 if (IS_ENVELOPE(element))
7663 local_player->show_envelope = element;
7668 case CA_SET_LEVEL_TIME:
7670 if (level.time > 0) /* only modify limited time value */
7672 TimeLeft = action_arg_number_new;
7674 DrawGameValue_Time(TimeLeft);
7676 if (!TimeLeft && setup.time_limit)
7677 for (i = 0; i < MAX_PLAYERS; i++)
7678 KillPlayer(&stored_player[i]);
7684 case CA_SET_LEVEL_SCORE:
7686 local_player->score = action_arg_number_new;
7688 DrawGameValue_Score(local_player->score);
7693 case CA_SET_LEVEL_GEMS:
7695 local_player->gems_still_needed = action_arg_number_new;
7697 DrawGameValue_Emeralds(local_player->gems_still_needed);
7702 case CA_SET_LEVEL_GRAVITY:
7704 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7705 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7706 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7711 case CA_SET_LEVEL_WIND:
7713 game.wind_direction = action_arg_direction;
7718 /* ---------- player actions ------------------------------------------ */
7720 case CA_MOVE_PLAYER:
7722 /* automatically move to the next field in specified direction */
7723 for (i = 0; i < MAX_PLAYERS; i++)
7724 if (trigger_player_bits & (1 << i))
7725 stored_player[i].programmed_action = action_arg_direction;
7730 case CA_EXIT_PLAYER:
7732 for (i = 0; i < MAX_PLAYERS; i++)
7733 if (action_arg_player_bits & (1 << i))
7734 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7739 case CA_KILL_PLAYER:
7741 for (i = 0; i < MAX_PLAYERS; i++)
7742 if (action_arg_player_bits & (1 << i))
7743 KillPlayer(&stored_player[i]);
7748 case CA_SET_PLAYER_KEYS:
7750 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7751 int element = getSpecialActionElement(action_arg_element,
7752 action_arg_number, EL_KEY_1);
7754 if (IS_KEY(element))
7756 for (i = 0; i < MAX_PLAYERS; i++)
7758 if (trigger_player_bits & (1 << i))
7760 stored_player[i].key[KEY_NR(element)] = key_state;
7762 DrawGameValue_Keys(stored_player[i].key);
7764 redraw_mask |= REDRAW_DOOR_1;
7772 case CA_SET_PLAYER_SPEED:
7774 for (i = 0; i < MAX_PLAYERS; i++)
7776 if (trigger_player_bits & (1 << i))
7778 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7780 if (action_arg == CA_ARG_SPEED_FASTER &&
7781 stored_player[i].cannot_move)
7783 action_arg_number = STEPSIZE_VERY_SLOW;
7785 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7786 action_arg == CA_ARG_SPEED_FASTER)
7788 action_arg_number = 2;
7789 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7794 getModifiedActionNumber(move_stepsize,
7797 action_arg_number_min,
7798 action_arg_number_max);
7801 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7803 /* make sure that value is power of 2 */
7804 move_stepsize = (1 << log_2(move_stepsize));
7806 /* do no immediately change -- the player might just be moving */
7807 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7809 stored_player[i].cannot_move =
7810 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7818 case CA_SET_PLAYER_SHIELD:
7820 for (i = 0; i < MAX_PLAYERS; i++)
7822 if (trigger_player_bits & (1 << i))
7824 if (action_arg == CA_ARG_SHIELD_OFF)
7826 stored_player[i].shield_normal_time_left = 0;
7827 stored_player[i].shield_deadly_time_left = 0;
7829 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7831 stored_player[i].shield_normal_time_left = 999999;
7833 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7835 stored_player[i].shield_normal_time_left = 999999;
7836 stored_player[i].shield_deadly_time_left = 999999;
7844 case CA_SET_PLAYER_ARTWORK:
7846 for (i = 0; i < MAX_PLAYERS; i++)
7848 if (trigger_player_bits & (1 << i))
7850 int artwork_element = action_arg_element;
7852 if (action_arg == CA_ARG_ELEMENT_RESET)
7854 (level.use_artwork_element[i] ? level.artwork_element[i] :
7855 stored_player[i].element_nr);
7857 stored_player[i].artwork_element = artwork_element;
7859 SetPlayerWaiting(&stored_player[i], FALSE);
7861 /* set number of special actions for bored and sleeping animation */
7862 stored_player[i].num_special_action_bored =
7863 get_num_special_action(artwork_element,
7864 ACTION_BORING_1, ACTION_BORING_LAST);
7865 stored_player[i].num_special_action_sleeping =
7866 get_num_special_action(artwork_element,
7867 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7874 /* ---------- CE actions ---------------------------------------------- */
7876 case CA_SET_CE_SCORE:
7878 ei->collect_score = action_arg_number_new;
7883 case CA_SET_CE_VALUE:
7885 #if USE_NEW_CUSTOM_VALUE
7886 int last_custom_value = CustomValue[x][y];
7888 CustomValue[x][y] = action_arg_number_new;
7891 printf("::: Count == %d\n", CustomValue[x][y]);
7894 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7897 printf("::: CE_VALUE_GETS_ZERO\n");
7900 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7901 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7904 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7912 /* ---------- engine actions ------------------------------------------ */
7914 case CA_SET_ENGINE_SCAN_MODE:
7916 InitPlayfieldScanMode(action_arg);
7926 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7928 int previous_move_direction = MovDir[x][y];
7929 #if USE_NEW_CUSTOM_VALUE
7930 int last_ce_value = CustomValue[x][y];
7932 boolean add_player = (ELEM_IS_PLAYER(element) &&
7933 IS_WALKABLE(Feld[x][y]));
7935 /* check if element under player changes from accessible to unaccessible
7936 (needed for special case of dropping element which then changes) */
7937 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7938 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7947 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7948 RemoveMovingField(x, y);
7952 Feld[x][y] = element;
7954 ResetGfxAnimation(x, y);
7955 ResetRandomAnimationValue(x, y);
7957 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7958 MovDir[x][y] = previous_move_direction;
7960 #if USE_NEW_CUSTOM_VALUE
7961 if (element_info[Feld[x][y]].use_last_ce_value)
7962 CustomValue[x][y] = last_ce_value;
7965 InitField_WithBug1(x, y, FALSE);
7967 DrawLevelField(x, y);
7969 if (GFX_CRUMBLED(Feld[x][y]))
7970 DrawLevelFieldCrumbledSandNeighbours(x, y);
7973 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7974 if (ELEM_IS_PLAYER(element))
7975 RelocatePlayer(x, y, element);
7978 ChangeCount[x][y]++; /* count number of changes in the same frame */
7980 TestIfBadThingTouchesPlayer(x, y);
7981 TestIfPlayerTouchesCustomElement(x, y);
7982 TestIfElementTouchesCustomElement(x, y);
7985 static void CreateField(int x, int y, int element)
7987 CreateFieldExt(x, y, element, FALSE);
7990 static void CreateElementFromChange(int x, int y, int element)
7992 element = GET_VALID_RUNTIME_ELEMENT(element);
7994 #if USE_STOP_CHANGED_ELEMENTS
7995 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7997 int old_element = Feld[x][y];
7999 /* prevent changed element from moving in same engine frame
8000 unless both old and new element can either fall or move */
8001 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8002 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8007 CreateFieldExt(x, y, element, TRUE);
8010 static boolean ChangeElement(int x, int y, int element, int page)
8012 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8014 int old_element = Feld[x][y];
8016 /* always use default change event to prevent running into a loop */
8017 if (ChangeEvent[x][y] == -1)
8018 ChangeEvent[x][y] = CE_DELAY;
8020 if (ChangeEvent[x][y] == CE_DELAY)
8022 /* reset actual trigger element, trigger player and action element */
8023 change->actual_trigger_element = EL_EMPTY;
8024 change->actual_trigger_player = EL_PLAYER_1;
8025 change->actual_trigger_side = CH_SIDE_NONE;
8026 change->actual_trigger_ce_value = 0;
8029 /* do not change elements more than a specified maximum number of changes */
8030 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8033 ChangeCount[x][y]++; /* count number of changes in the same frame */
8035 if (change->explode)
8042 if (change->use_target_content)
8044 boolean complete_replace = TRUE;
8045 boolean can_replace[3][3];
8048 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8051 boolean is_walkable;
8052 boolean is_diggable;
8053 boolean is_collectible;
8054 boolean is_removable;
8055 boolean is_destructible;
8056 int ex = x + xx - 1;
8057 int ey = y + yy - 1;
8058 int content_element = change->target_content.e[xx][yy];
8061 can_replace[xx][yy] = TRUE;
8063 if (ex == x && ey == y) /* do not check changing element itself */
8066 if (content_element == EL_EMPTY_SPACE)
8068 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8073 if (!IN_LEV_FIELD(ex, ey))
8075 can_replace[xx][yy] = FALSE;
8076 complete_replace = FALSE;
8083 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8084 e = MovingOrBlocked2Element(ex, ey);
8086 is_empty = (IS_FREE(ex, ey) ||
8087 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8089 is_walkable = (is_empty || IS_WALKABLE(e));
8090 is_diggable = (is_empty || IS_DIGGABLE(e));
8091 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8092 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8093 is_removable = (is_diggable || is_collectible);
8095 can_replace[xx][yy] =
8096 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8097 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8098 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8099 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8100 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8101 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8102 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8104 if (!can_replace[xx][yy])
8105 complete_replace = FALSE;
8108 if (!change->only_if_complete || complete_replace)
8110 boolean something_has_changed = FALSE;
8112 if (change->only_if_complete && change->use_random_replace &&
8113 RND(100) < change->random_percentage)
8116 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8118 int ex = x + xx - 1;
8119 int ey = y + yy - 1;
8120 int content_element;
8122 if (can_replace[xx][yy] && (!change->use_random_replace ||
8123 RND(100) < change->random_percentage))
8125 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8126 RemoveMovingField(ex, ey);
8128 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8130 content_element = change->target_content.e[xx][yy];
8131 target_element = GET_TARGET_ELEMENT(content_element, change);
8133 CreateElementFromChange(ex, ey, target_element);
8135 something_has_changed = TRUE;
8137 /* for symmetry reasons, freeze newly created border elements */
8138 if (ex != x || ey != y)
8139 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8143 if (something_has_changed)
8145 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8146 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8152 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8154 if (element == EL_DIAGONAL_GROWING ||
8155 element == EL_DIAGONAL_SHRINKING)
8157 target_element = Store[x][y];
8159 Store[x][y] = EL_EMPTY;
8162 CreateElementFromChange(x, y, target_element);
8164 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8165 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8168 /* this uses direct change before indirect change */
8169 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8174 #if USE_NEW_DELAYED_ACTION
8176 static void HandleElementChange(int x, int y, int page)
8178 int element = MovingOrBlocked2Element(x, y);
8179 struct ElementInfo *ei = &element_info[element];
8180 struct ElementChangeInfo *change = &ei->change_page[page];
8183 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8184 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8187 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8188 x, y, element, element_info[element].token_name);
8189 printf("HandleElementChange(): This should never happen!\n");
8194 /* this can happen with classic bombs on walkable, changing elements */
8195 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8198 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8199 ChangeDelay[x][y] = 0;
8205 if (ChangeDelay[x][y] == 0) /* initialize element change */
8207 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8209 if (change->can_change)
8211 ResetGfxAnimation(x, y);
8212 ResetRandomAnimationValue(x, y);
8214 if (change->pre_change_function)
8215 change->pre_change_function(x, y);
8219 ChangeDelay[x][y]--;
8221 if (ChangeDelay[x][y] != 0) /* continue element change */
8223 if (change->can_change)
8225 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8227 if (IS_ANIMATED(graphic))
8228 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8230 if (change->change_function)
8231 change->change_function(x, y);
8234 else /* finish element change */
8236 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8238 page = ChangePage[x][y];
8239 ChangePage[x][y] = -1;
8241 change = &ei->change_page[page];
8244 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8246 ChangeDelay[x][y] = 1; /* try change after next move step */
8247 ChangePage[x][y] = page; /* remember page to use for change */
8252 if (change->can_change)
8254 if (ChangeElement(x, y, element, page))
8256 if (change->post_change_function)
8257 change->post_change_function(x, y);
8261 if (change->has_action)
8262 ExecuteCustomElementAction(x, y, element, page);
8268 static void HandleElementChange(int x, int y, int page)
8270 int element = MovingOrBlocked2Element(x, y);
8271 struct ElementInfo *ei = &element_info[element];
8272 struct ElementChangeInfo *change = &ei->change_page[page];
8275 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8278 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8279 x, y, element, element_info[element].token_name);
8280 printf("HandleElementChange(): This should never happen!\n");
8285 /* this can happen with classic bombs on walkable, changing elements */
8286 if (!CAN_CHANGE(element))
8289 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8290 ChangeDelay[x][y] = 0;
8296 if (ChangeDelay[x][y] == 0) /* initialize element change */
8298 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8300 ResetGfxAnimation(x, y);
8301 ResetRandomAnimationValue(x, y);
8303 if (change->pre_change_function)
8304 change->pre_change_function(x, y);
8307 ChangeDelay[x][y]--;
8309 if (ChangeDelay[x][y] != 0) /* continue element change */
8311 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8313 if (IS_ANIMATED(graphic))
8314 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8316 if (change->change_function)
8317 change->change_function(x, y);
8319 else /* finish element change */
8321 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8323 page = ChangePage[x][y];
8324 ChangePage[x][y] = -1;
8326 change = &ei->change_page[page];
8329 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8331 ChangeDelay[x][y] = 1; /* try change after next move step */
8332 ChangePage[x][y] = page; /* remember page to use for change */
8337 if (ChangeElement(x, y, element, page))
8339 if (change->post_change_function)
8340 change->post_change_function(x, y);
8347 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8348 int trigger_element,
8354 boolean change_done_any = FALSE;
8355 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8358 if (!(trigger_events[trigger_element][trigger_event]))
8361 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8363 int element = EL_CUSTOM_START + i;
8364 boolean change_done = FALSE;
8367 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8368 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8371 for (p = 0; p < element_info[element].num_change_pages; p++)
8373 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8375 if (change->can_change_or_has_action &&
8376 change->has_event[trigger_event] &&
8377 change->trigger_side & trigger_side &&
8378 change->trigger_player & trigger_player &&
8379 change->trigger_page & trigger_page_bits &&
8380 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8382 change->actual_trigger_element = trigger_element;
8383 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8384 change->actual_trigger_side = trigger_side;
8385 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8387 if ((change->can_change && !change_done) || change->has_action)
8392 SCAN_PLAYFIELD(x, y)
8394 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8397 if (Feld[x][y] == element)
8399 if (change->can_change && !change_done)
8401 ChangeDelay[x][y] = 1;
8402 ChangeEvent[x][y] = trigger_event;
8404 HandleElementChange(x, y, p);
8406 #if USE_NEW_DELAYED_ACTION
8407 else if (change->has_action)
8409 ExecuteCustomElementAction(x, y, element, p);
8410 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8413 if (change->has_action)
8415 ExecuteCustomElementAction(x, y, element, p);
8416 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8422 if (change->can_change)
8425 change_done_any = TRUE;
8432 return change_done_any;
8435 static boolean CheckElementChangeExt(int x, int y,
8437 int trigger_element,
8442 boolean change_done = FALSE;
8445 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8446 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8449 if (Feld[x][y] == EL_BLOCKED)
8451 Blocked2Moving(x, y, &x, &y);
8452 element = Feld[x][y];
8456 /* check if element has already changed */
8457 if (Feld[x][y] != element)
8460 /* check if element has already changed or is about to change after moving */
8461 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8462 Feld[x][y] != element) ||
8464 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8465 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8466 ChangePage[x][y] != -1)))
8470 for (p = 0; p < element_info[element].num_change_pages; p++)
8472 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8474 boolean check_trigger_element =
8475 (trigger_event == CE_TOUCHING_X ||
8476 trigger_event == CE_HITTING_X ||
8477 trigger_event == CE_HIT_BY_X);
8479 if (change->can_change_or_has_action &&
8480 change->has_event[trigger_event] &&
8481 change->trigger_side & trigger_side &&
8482 change->trigger_player & trigger_player &&
8483 (!check_trigger_element ||
8484 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8486 change->actual_trigger_element = trigger_element;
8487 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8488 change->actual_trigger_side = trigger_side;
8489 change->actual_trigger_ce_value = CustomValue[x][y];
8491 /* special case: trigger element not at (x,y) position for some events */
8492 if (check_trigger_element)
8504 { 0, 0 }, { 0, 0 }, { 0, 0 },
8508 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8509 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8511 change->actual_trigger_ce_value = CustomValue[xx][yy];
8514 if (change->can_change && !change_done)
8516 ChangeDelay[x][y] = 1;
8517 ChangeEvent[x][y] = trigger_event;
8519 HandleElementChange(x, y, p);
8523 #if USE_NEW_DELAYED_ACTION
8524 else if (change->has_action)
8526 ExecuteCustomElementAction(x, y, element, p);
8527 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8530 if (change->has_action)
8532 ExecuteCustomElementAction(x, y, element, p);
8533 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8542 static void PlayPlayerSound(struct PlayerInfo *player)
8544 int jx = player->jx, jy = player->jy;
8545 int sound_element = player->artwork_element;
8546 int last_action = player->last_action_waiting;
8547 int action = player->action_waiting;
8549 if (player->is_waiting)
8551 if (action != last_action)
8552 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8554 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8558 if (action != last_action)
8559 StopSound(element_info[sound_element].sound[last_action]);
8561 if (last_action == ACTION_SLEEPING)
8562 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8566 static void PlayAllPlayersSound()
8570 for (i = 0; i < MAX_PLAYERS; i++)
8571 if (stored_player[i].active)
8572 PlayPlayerSound(&stored_player[i]);
8575 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8577 boolean last_waiting = player->is_waiting;
8578 int move_dir = player->MovDir;
8580 player->last_action_waiting = player->action_waiting;
8584 if (!last_waiting) /* not waiting -> waiting */
8586 player->is_waiting = TRUE;
8588 player->frame_counter_bored =
8590 game.player_boring_delay_fixed +
8591 SimpleRND(game.player_boring_delay_random);
8592 player->frame_counter_sleeping =
8594 game.player_sleeping_delay_fixed +
8595 SimpleRND(game.player_sleeping_delay_random);
8597 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8600 if (game.player_sleeping_delay_fixed +
8601 game.player_sleeping_delay_random > 0 &&
8602 player->anim_delay_counter == 0 &&
8603 player->post_delay_counter == 0 &&
8604 FrameCounter >= player->frame_counter_sleeping)
8605 player->is_sleeping = TRUE;
8606 else if (game.player_boring_delay_fixed +
8607 game.player_boring_delay_random > 0 &&
8608 FrameCounter >= player->frame_counter_bored)
8609 player->is_bored = TRUE;
8611 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8612 player->is_bored ? ACTION_BORING :
8615 if (player->is_sleeping)
8617 if (player->num_special_action_sleeping > 0)
8619 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8621 int last_special_action = player->special_action_sleeping;
8622 int num_special_action = player->num_special_action_sleeping;
8623 int special_action =
8624 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8625 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8626 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8627 last_special_action + 1 : ACTION_SLEEPING);
8628 int special_graphic =
8629 el_act_dir2img(player->artwork_element, special_action, move_dir);
8631 player->anim_delay_counter =
8632 graphic_info[special_graphic].anim_delay_fixed +
8633 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8634 player->post_delay_counter =
8635 graphic_info[special_graphic].post_delay_fixed +
8636 SimpleRND(graphic_info[special_graphic].post_delay_random);
8638 player->special_action_sleeping = special_action;
8641 if (player->anim_delay_counter > 0)
8643 player->action_waiting = player->special_action_sleeping;
8644 player->anim_delay_counter--;
8646 else if (player->post_delay_counter > 0)
8648 player->post_delay_counter--;
8652 else if (player->is_bored)
8654 if (player->num_special_action_bored > 0)
8656 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8658 int special_action =
8659 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8660 int special_graphic =
8661 el_act_dir2img(player->artwork_element, special_action, move_dir);
8663 player->anim_delay_counter =
8664 graphic_info[special_graphic].anim_delay_fixed +
8665 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8666 player->post_delay_counter =
8667 graphic_info[special_graphic].post_delay_fixed +
8668 SimpleRND(graphic_info[special_graphic].post_delay_random);
8670 player->special_action_bored = special_action;
8673 if (player->anim_delay_counter > 0)
8675 player->action_waiting = player->special_action_bored;
8676 player->anim_delay_counter--;
8678 else if (player->post_delay_counter > 0)
8680 player->post_delay_counter--;
8685 else if (last_waiting) /* waiting -> not waiting */
8687 player->is_waiting = FALSE;
8688 player->is_bored = FALSE;
8689 player->is_sleeping = FALSE;
8691 player->frame_counter_bored = -1;
8692 player->frame_counter_sleeping = -1;
8694 player->anim_delay_counter = 0;
8695 player->post_delay_counter = 0;
8697 player->action_waiting = ACTION_DEFAULT;
8699 player->special_action_bored = ACTION_DEFAULT;
8700 player->special_action_sleeping = ACTION_DEFAULT;
8704 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8706 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8707 int left = player_action & JOY_LEFT;
8708 int right = player_action & JOY_RIGHT;
8709 int up = player_action & JOY_UP;
8710 int down = player_action & JOY_DOWN;
8711 int button1 = player_action & JOY_BUTTON_1;
8712 int button2 = player_action & JOY_BUTTON_2;
8713 int dx = (left ? -1 : right ? 1 : 0);
8714 int dy = (up ? -1 : down ? 1 : 0);
8716 if (!player->active || tape.pausing)
8722 snapped = SnapField(player, dx, dy);
8726 dropped = DropElement(player);
8728 moved = MovePlayer(player, dx, dy);
8731 if (tape.single_step && tape.recording && !tape.pausing)
8733 if (button1 || (dropped && !moved))
8735 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8736 SnapField(player, 0, 0); /* stop snapping */
8740 SetPlayerWaiting(player, FALSE);
8742 return player_action;
8746 /* no actions for this player (no input at player's configured device) */
8748 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8749 SnapField(player, 0, 0);
8750 CheckGravityMovementWhenNotMoving(player);
8752 if (player->MovPos == 0)
8753 SetPlayerWaiting(player, TRUE);
8755 if (player->MovPos == 0) /* needed for tape.playing */
8756 player->is_moving = FALSE;
8758 player->is_dropping = FALSE;
8764 void AdvanceFrameAndPlayerCounters(int player_nr)
8768 /* advance frame counters (global frame counter and time frame counter) */
8772 /* advance player counters (counters for move delay, move animation etc.) */
8773 for (i = 0; i < MAX_PLAYERS; i++)
8775 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8776 int move_delay_value = stored_player[i].move_delay_value;
8777 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8779 if (!advance_player_counters) /* not all players may be affected */
8782 #if USE_NEW_PLAYER_ANIM
8783 if (move_frames == 0) /* less than one move per game frame */
8785 int stepsize = TILEX / move_delay_value;
8786 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8787 int count = (stored_player[i].is_moving ?
8788 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8790 if (count % delay == 0)
8795 stored_player[i].Frame += move_frames;
8797 if (stored_player[i].MovPos != 0)
8798 stored_player[i].StepFrame += move_frames;
8800 if (stored_player[i].move_delay > 0)
8801 stored_player[i].move_delay--;
8803 /* due to bugs in previous versions, counter must count up, not down */
8804 if (stored_player[i].push_delay != -1)
8805 stored_player[i].push_delay++;
8807 if (stored_player[i].drop_delay > 0)
8808 stored_player[i].drop_delay--;
8814 static unsigned long game_frame_delay = 0;
8815 unsigned long game_frame_delay_value;
8816 int magic_wall_x = 0, magic_wall_y = 0;
8817 int i, x, y, element, graphic;
8818 byte *recorded_player_action;
8819 byte summarized_player_action = 0;
8820 byte tape_action[MAX_PLAYERS];
8822 if (game_status != GAME_MODE_PLAYING)
8825 game_frame_delay_value =
8826 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8828 if (tape.playing && tape.warp_forward && !tape.pausing)
8829 game_frame_delay_value = 0;
8831 /* ---------- main game synchronization point ---------- */
8833 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8835 InitPlayfieldScanModeVars();
8837 if (ScreenMovPos == 0) /* screen currently aligned at tile position */
8839 struct PlayerInfo *player;
8840 int player_nr = game.centered_player_nr_next;
8842 if (game.centered_player_nr_next == -1)
8843 player_nr = local_player->index_nr;
8845 player = &stored_player[player_nr];
8847 if (!player->active)
8848 game.centered_player_nr_next = game.centered_player_nr;
8850 if (game.centered_player_nr != game.centered_player_nr_next)
8852 DrawRelocatePlayer(player, setup.quick_switch);
8854 game.centered_player_nr = game.centered_player_nr_next;
8858 #if USE_ONE_MORE_CHANGE_PER_FRAME
8859 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8861 SCAN_PLAYFIELD(x, y)
8863 ChangeCount[x][y] = 0;
8864 ChangeEvent[x][y] = -1;
8869 if (network_playing && !network_player_action_received)
8871 /* try to get network player actions in time */
8873 #if defined(NETWORK_AVALIABLE)
8874 /* last chance to get network player actions without main loop delay */
8878 /* game was quit by network peer */
8879 if (game_status != GAME_MODE_PLAYING)
8882 if (!network_player_action_received)
8883 return; /* failed to get network player actions in time */
8889 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8892 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8893 if (recorded_player_action == NULL && tape.pausing)
8897 for (i = 0; i < MAX_PLAYERS; i++)
8899 summarized_player_action |= stored_player[i].action;
8901 if (!network_playing)
8902 stored_player[i].effective_action = stored_player[i].action;
8905 #if defined(NETWORK_AVALIABLE)
8906 if (network_playing)
8907 SendToServer_MovePlayer(summarized_player_action);
8910 if (!options.network && !setup.team_mode)
8911 local_player->effective_action = summarized_player_action;
8913 if (recorded_player_action != NULL)
8914 for (i = 0; i < MAX_PLAYERS; i++)
8915 stored_player[i].effective_action = recorded_player_action[i];
8917 for (i = 0; i < MAX_PLAYERS; i++)
8919 tape_action[i] = stored_player[i].effective_action;
8921 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8922 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8925 /* only save actions from input devices, but not programmed actions */
8927 TapeRecordAction(tape_action);
8929 for (i = 0; i < MAX_PLAYERS; i++)
8931 int actual_player_action = stored_player[i].effective_action;
8934 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8935 - rnd_equinox_tetrachloride 048
8936 - rnd_equinox_tetrachloride_ii 096
8937 - rnd_emanuel_schmieg 002
8938 - doctor_sloan_ww 001, 020
8940 if (stored_player[i].MovPos == 0)
8941 CheckGravityMovement(&stored_player[i]);
8944 /* overwrite programmed action with tape action */
8945 if (stored_player[i].programmed_action)
8946 actual_player_action = stored_player[i].programmed_action;
8949 PlayerActions(&stored_player[i], actual_player_action);
8951 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8953 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8954 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8957 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8960 network_player_action_received = FALSE;
8962 ScrollScreen(NULL, SCROLL_GO_ON);
8964 /* for backwards compatibility, the following code emulates a fixed bug that
8965 occured when pushing elements (causing elements that just made their last
8966 pushing step to already (if possible) make their first falling step in the
8967 same game frame, which is bad); this code is also needed to use the famous
8968 "spring push bug" which is used in older levels and might be wanted to be
8969 used also in newer levels, but in this case the buggy pushing code is only
8970 affecting the "spring" element and no other elements */
8972 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8974 for (i = 0; i < MAX_PLAYERS; i++)
8976 struct PlayerInfo *player = &stored_player[i];
8980 if (player->active && player->is_pushing && player->is_moving &&
8982 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8983 Feld[x][y] == EL_SPRING))
8985 ContinueMoving(x, y);
8987 /* continue moving after pushing (this is actually a bug) */
8988 if (!IS_MOVING(x, y))
8997 SCAN_PLAYFIELD(x, y)
8999 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9002 ChangeCount[x][y] = 0;
9003 ChangeEvent[x][y] = -1;
9005 /* this must be handled before main playfield loop */
9006 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9009 if (MovDelay[x][y] <= 0)
9013 #if USE_NEW_SNAP_DELAY
9014 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9017 if (MovDelay[x][y] <= 0)
9020 DrawLevelField(x, y);
9022 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9028 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9030 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9031 printf("GameActions(): This should never happen!\n");
9033 ChangePage[x][y] = -1;
9038 if (WasJustMoving[x][y] > 0)
9039 WasJustMoving[x][y]--;
9040 if (WasJustFalling[x][y] > 0)
9041 WasJustFalling[x][y]--;
9042 if (CheckCollision[x][y] > 0)
9043 CheckCollision[x][y]--;
9047 /* reset finished pushing action (not done in ContinueMoving() to allow
9048 continuous pushing animation for elements with zero push delay) */
9049 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9051 ResetGfxAnimation(x, y);
9052 DrawLevelField(x, y);
9056 if (IS_BLOCKED(x, y))
9060 Blocked2Moving(x, y, &oldx, &oldy);
9061 if (!IS_MOVING(oldx, oldy))
9063 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9064 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9065 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9066 printf("GameActions(): This should never happen!\n");
9073 SCAN_PLAYFIELD(x, y)
9075 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9078 element = Feld[x][y];
9079 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9082 printf("::: %d,%d\n", x, y);
9084 if (element == EL_ROCK)
9085 printf("::: Yo man! Rocks can fall!\n");
9088 if (graphic_info[graphic].anim_global_sync)
9089 GfxFrame[x][y] = FrameCounter;
9090 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9092 int old_gfx_frame = GfxFrame[x][y];
9094 GfxFrame[x][y] = CustomValue[x][y];
9097 if (GfxFrame[x][y] != old_gfx_frame)
9099 DrawLevelGraphicAnimation(x, y, graphic);
9101 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9103 int old_gfx_frame = GfxFrame[x][y];
9105 GfxFrame[x][y] = element_info[element].collect_score;
9108 if (GfxFrame[x][y] != old_gfx_frame)
9110 DrawLevelGraphicAnimation(x, y, graphic);
9113 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9114 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9115 ResetRandomAnimationValue(x, y);
9117 SetRandomAnimationValue(x, y);
9119 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9121 if (IS_INACTIVE(element))
9123 if (IS_ANIMATED(graphic))
9124 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9129 /* this may take place after moving, so 'element' may have changed */
9130 if (IS_CHANGING(x, y) &&
9131 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9133 int page = element_info[element].event_page_nr[CE_DELAY];
9135 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9139 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9143 if (element == EL_CUSTOM_255)
9144 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9148 HandleElementChange(x, y, page);
9150 if (CAN_CHANGE(element))
9151 HandleElementChange(x, y, page);
9153 if (HAS_ACTION(element))
9154 ExecuteCustomElementAction(x, y, element, page);
9159 element = Feld[x][y];
9160 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9163 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9167 element = Feld[x][y];
9168 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9170 if (IS_ANIMATED(graphic) &&
9173 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9175 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9176 EdelsteinFunkeln(x, y);
9178 else if ((element == EL_ACID ||
9179 element == EL_EXIT_OPEN ||
9180 element == EL_SP_EXIT_OPEN ||
9181 element == EL_SP_TERMINAL ||
9182 element == EL_SP_TERMINAL_ACTIVE ||
9183 element == EL_EXTRA_TIME ||
9184 element == EL_SHIELD_NORMAL ||
9185 element == EL_SHIELD_DEADLY) &&
9186 IS_ANIMATED(graphic))
9187 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9188 else if (IS_MOVING(x, y))
9189 ContinueMoving(x, y);
9190 else if (IS_ACTIVE_BOMB(element))
9191 CheckDynamite(x, y);
9192 else if (element == EL_AMOEBA_GROWING)
9193 AmoebeWaechst(x, y);
9194 else if (element == EL_AMOEBA_SHRINKING)
9195 AmoebaDisappearing(x, y);
9197 #if !USE_NEW_AMOEBA_CODE
9198 else if (IS_AMOEBALIVE(element))
9199 AmoebeAbleger(x, y);
9202 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9204 else if (element == EL_EXIT_CLOSED)
9206 else if (element == EL_SP_EXIT_CLOSED)
9208 else if (element == EL_EXPANDABLE_WALL_GROWING)
9210 else if (element == EL_EXPANDABLE_WALL ||
9211 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9212 element == EL_EXPANDABLE_WALL_VERTICAL ||
9213 element == EL_EXPANDABLE_WALL_ANY)
9215 else if (element == EL_FLAMES)
9216 CheckForDragon(x, y);
9217 else if (element == EL_EXPLOSION)
9218 ; /* drawing of correct explosion animation is handled separately */
9219 else if (element == EL_ELEMENT_SNAPPING ||
9220 element == EL_DIAGONAL_SHRINKING ||
9221 element == EL_DIAGONAL_GROWING)
9224 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9226 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9229 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9230 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9233 if (element == EL_CUSTOM_255 ||
9234 element == EL_CUSTOM_256)
9235 DrawLevelGraphicAnimation(x, y, graphic);
9238 if (IS_BELT_ACTIVE(element))
9239 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9241 if (game.magic_wall_active)
9243 int jx = local_player->jx, jy = local_player->jy;
9245 /* play the element sound at the position nearest to the player */
9246 if ((element == EL_MAGIC_WALL_FULL ||
9247 element == EL_MAGIC_WALL_ACTIVE ||
9248 element == EL_MAGIC_WALL_EMPTYING ||
9249 element == EL_BD_MAGIC_WALL_FULL ||
9250 element == EL_BD_MAGIC_WALL_ACTIVE ||
9251 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9252 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9260 #if USE_NEW_AMOEBA_CODE
9261 /* new experimental amoeba growth stuff */
9262 if (!(FrameCounter % 8))
9264 static unsigned long random = 1684108901;
9266 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9268 x = RND(lev_fieldx);
9269 y = RND(lev_fieldy);
9270 element = Feld[x][y];
9272 if (!IS_PLAYER(x,y) &&
9273 (element == EL_EMPTY ||
9274 CAN_GROW_INTO(element) ||
9275 element == EL_QUICKSAND_EMPTY ||
9276 element == EL_ACID_SPLASH_LEFT ||
9277 element == EL_ACID_SPLASH_RIGHT))
9279 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9280 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9281 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9282 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9283 Feld[x][y] = EL_AMOEBA_DROP;
9286 random = random * 129 + 1;
9292 if (game.explosions_delayed)
9295 game.explosions_delayed = FALSE;
9298 SCAN_PLAYFIELD(x, y)
9300 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9303 element = Feld[x][y];
9305 if (ExplodeField[x][y])
9306 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9307 else if (element == EL_EXPLOSION)
9308 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9310 ExplodeField[x][y] = EX_TYPE_NONE;
9313 game.explosions_delayed = TRUE;
9316 if (game.magic_wall_active)
9318 if (!(game.magic_wall_time_left % 4))
9320 int element = Feld[magic_wall_x][magic_wall_y];
9322 if (element == EL_BD_MAGIC_WALL_FULL ||
9323 element == EL_BD_MAGIC_WALL_ACTIVE ||
9324 element == EL_BD_MAGIC_WALL_EMPTYING)
9325 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9327 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9330 if (game.magic_wall_time_left > 0)
9332 game.magic_wall_time_left--;
9333 if (!game.magic_wall_time_left)
9336 SCAN_PLAYFIELD(x, y)
9338 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9341 element = Feld[x][y];
9343 if (element == EL_MAGIC_WALL_ACTIVE ||
9344 element == EL_MAGIC_WALL_FULL)
9346 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9347 DrawLevelField(x, y);
9349 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9350 element == EL_BD_MAGIC_WALL_FULL)
9352 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9353 DrawLevelField(x, y);
9357 game.magic_wall_active = FALSE;
9362 if (game.light_time_left > 0)
9364 game.light_time_left--;
9366 if (game.light_time_left == 0)
9367 RedrawAllLightSwitchesAndInvisibleElements();
9370 if (game.timegate_time_left > 0)
9372 game.timegate_time_left--;
9374 if (game.timegate_time_left == 0)
9375 CloseAllOpenTimegates();
9378 if (game.lenses_time_left > 0)
9380 game.lenses_time_left--;
9382 if (game.lenses_time_left == 0)
9383 RedrawAllInvisibleElementsForLenses();
9386 if (game.magnify_time_left > 0)
9388 game.magnify_time_left--;
9390 if (game.magnify_time_left == 0)
9391 RedrawAllInvisibleElementsForMagnifier();
9394 for (i = 0; i < MAX_PLAYERS; i++)
9396 struct PlayerInfo *player = &stored_player[i];
9398 if (SHIELD_ON(player))
9400 if (player->shield_deadly_time_left)
9401 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9402 else if (player->shield_normal_time_left)
9403 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9407 if (TimeFrames >= FRAMES_PER_SECOND)
9412 for (i = 0; i < MAX_PLAYERS; i++)
9414 struct PlayerInfo *player = &stored_player[i];
9416 if (SHIELD_ON(player))
9418 player->shield_normal_time_left--;
9420 if (player->shield_deadly_time_left > 0)
9421 player->shield_deadly_time_left--;
9425 if (!level.use_step_counter)
9433 if (TimeLeft <= 10 && setup.time_limit)
9434 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9436 DrawGameValue_Time(TimeLeft);
9438 if (!TimeLeft && setup.time_limit)
9439 for (i = 0; i < MAX_PLAYERS; i++)
9440 KillPlayer(&stored_player[i]);
9442 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9443 DrawGameValue_Time(TimePlayed);
9446 if (tape.recording || tape.playing)
9447 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9451 PlayAllPlayersSound();
9453 if (options.debug) /* calculate frames per second */
9455 static unsigned long fps_counter = 0;
9456 static int fps_frames = 0;
9457 unsigned long fps_delay_ms = Counter() - fps_counter;
9461 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9463 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9466 fps_counter = Counter();
9469 redraw_mask |= REDRAW_FPS;
9472 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9474 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9476 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9478 local_player->show_envelope = 0;
9481 /* use random number generator in every frame to make it less predictable */
9482 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9486 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9488 int min_x = x, min_y = y, max_x = x, max_y = y;
9491 for (i = 0; i < MAX_PLAYERS; i++)
9493 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9495 if (!stored_player[i].active || &stored_player[i] == player)
9498 min_x = MIN(min_x, jx);
9499 min_y = MIN(min_y, jy);
9500 max_x = MAX(max_x, jx);
9501 max_y = MAX(max_y, jy);
9504 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9507 static boolean AllPlayersInVisibleScreen()
9511 for (i = 0; i < MAX_PLAYERS; i++)
9513 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9515 if (!stored_player[i].active)
9518 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9525 void ScrollLevel(int dx, int dy)
9527 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9530 BlitBitmap(drawto_field, drawto_field,
9531 FX + TILEX * (dx == -1) - softscroll_offset,
9532 FY + TILEY * (dy == -1) - softscroll_offset,
9533 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9534 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9535 FX + TILEX * (dx == 1) - softscroll_offset,
9536 FY + TILEY * (dy == 1) - softscroll_offset);
9540 x = (dx == 1 ? BX1 : BX2);
9541 for (y = BY1; y <= BY2; y++)
9542 DrawScreenField(x, y);
9547 y = (dy == 1 ? BY1 : BY2);
9548 for (x = BX1; x <= BX2; x++)
9549 DrawScreenField(x, y);
9552 redraw_mask |= REDRAW_FIELD;
9555 static boolean canFallDown(struct PlayerInfo *player)
9557 int jx = player->jx, jy = player->jy;
9559 return (IN_LEV_FIELD(jx, jy + 1) &&
9560 (IS_FREE(jx, jy + 1) ||
9561 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9562 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9563 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9566 static boolean canPassField(int x, int y, int move_dir)
9568 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9569 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9570 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9573 int element = Feld[x][y];
9575 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9576 !CAN_MOVE(element) &&
9577 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9578 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9579 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9582 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9584 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9585 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9586 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9590 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9591 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9592 (IS_DIGGABLE(Feld[newx][newy]) ||
9593 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9594 canPassField(newx, newy, move_dir)));
9597 static void CheckGravityMovement(struct PlayerInfo *player)
9599 if (game.gravity && !player->programmed_action)
9601 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9602 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9603 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9604 int jx = player->jx, jy = player->jy;
9605 boolean player_is_moving_to_valid_field =
9606 (!player_is_snapping &&
9607 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9608 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9609 boolean player_can_fall_down = canFallDown(player);
9611 if (player_can_fall_down &&
9612 !player_is_moving_to_valid_field)
9613 player->programmed_action = MV_DOWN;
9617 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9619 return CheckGravityMovement(player);
9621 if (game.gravity && !player->programmed_action)
9623 int jx = player->jx, jy = player->jy;
9624 boolean field_under_player_is_free =
9625 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9626 boolean player_is_standing_on_valid_field =
9627 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9628 (IS_WALKABLE(Feld[jx][jy]) &&
9629 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9631 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9632 player->programmed_action = MV_DOWN;
9638 -----------------------------------------------------------------------------
9639 dx, dy: direction (non-diagonal) to try to move the player to
9640 real_dx, real_dy: direction as read from input device (can be diagonal)
9643 boolean MovePlayerOneStep(struct PlayerInfo *player,
9644 int dx, int dy, int real_dx, int real_dy)
9646 int jx = player->jx, jy = player->jy;
9647 int new_jx = jx + dx, new_jy = jy + dy;
9648 #if !USE_FIXED_DONT_RUN_INTO
9652 boolean player_can_move = !player->cannot_move;
9654 if (!player->active || (!dx && !dy))
9655 return MP_NO_ACTION;
9657 player->MovDir = (dx < 0 ? MV_LEFT :
9660 dy > 0 ? MV_DOWN : MV_NONE);
9662 if (!IN_LEV_FIELD(new_jx, new_jy))
9663 return MP_NO_ACTION;
9665 if (!player_can_move)
9668 if (player->MovPos == 0)
9670 player->is_moving = FALSE;
9671 player->is_digging = FALSE;
9672 player->is_collecting = FALSE;
9673 player->is_snapping = FALSE;
9674 player->is_pushing = FALSE;
9677 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9678 SnapField(player, 0, 0);
9682 return MP_NO_ACTION;
9687 if (!options.network && game.centered_player_nr == -1 &&
9688 !AllPlayersInSight(player, new_jx, new_jy))
9689 return MP_NO_ACTION;
9691 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9692 return MP_NO_ACTION;
9695 #if !USE_FIXED_DONT_RUN_INTO
9696 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9698 /* (moved to DigField()) */
9699 if (player_can_move && DONT_RUN_INTO(element))
9701 if (element == EL_ACID && dx == 0 && dy == 1)
9703 SplashAcid(new_jx, new_jy);
9704 Feld[jx][jy] = EL_PLAYER_1;
9705 InitMovingField(jx, jy, MV_DOWN);
9706 Store[jx][jy] = EL_ACID;
9707 ContinueMoving(jx, jy);
9711 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9717 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9719 #if USE_FIXED_DONT_RUN_INTO
9720 if (can_move == MP_DONT_RUN_INTO)
9724 if (can_move != MP_MOVING)
9727 #if USE_FIXED_DONT_RUN_INTO
9730 /* check if DigField() has caused relocation of the player */
9731 if (player->jx != jx || player->jy != jy)
9732 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9734 StorePlayer[jx][jy] = 0;
9735 player->last_jx = jx;
9736 player->last_jy = jy;
9737 player->jx = new_jx;
9738 player->jy = new_jy;
9739 StorePlayer[new_jx][new_jy] = player->element_nr;
9741 if (player->move_delay_value_next != -1)
9743 player->move_delay_value = player->move_delay_value_next;
9744 player->move_delay_value_next = -1;
9748 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9750 player->step_counter++;
9752 PlayerVisit[jx][jy] = FrameCounter;
9754 ScrollPlayer(player, SCROLL_INIT);
9759 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9761 int jx = player->jx, jy = player->jy;
9762 int old_jx = jx, old_jy = jy;
9763 int moved = MP_NO_ACTION;
9765 if (!player->active)
9770 if (player->MovPos == 0)
9772 player->is_moving = FALSE;
9773 player->is_digging = FALSE;
9774 player->is_collecting = FALSE;
9775 player->is_snapping = FALSE;
9776 player->is_pushing = FALSE;
9782 if (player->move_delay > 0)
9785 player->move_delay = -1; /* set to "uninitialized" value */
9787 /* store if player is automatically moved to next field */
9788 player->is_auto_moving = (player->programmed_action != MV_NONE);
9790 /* remove the last programmed player action */
9791 player->programmed_action = 0;
9795 /* should only happen if pre-1.2 tape recordings are played */
9796 /* this is only for backward compatibility */
9798 int original_move_delay_value = player->move_delay_value;
9801 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9805 /* scroll remaining steps with finest movement resolution */
9806 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9808 while (player->MovPos)
9810 ScrollPlayer(player, SCROLL_GO_ON);
9811 ScrollScreen(NULL, SCROLL_GO_ON);
9813 AdvanceFrameAndPlayerCounters(player->index_nr);
9819 player->move_delay_value = original_move_delay_value;
9822 if (player->last_move_dir & MV_HORIZONTAL)
9824 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9825 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9829 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9830 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9837 if (moved & MP_MOVING && !ScreenMovPos &&
9838 (player->index_nr == game.centered_player_nr ||
9839 game.centered_player_nr == -1))
9841 if (moved & MP_MOVING && !ScreenMovPos &&
9842 (player == local_player || !options.network))
9845 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9846 int offset = (setup.scroll_delay ? 3 : 0);
9848 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9850 /* actual player has left the screen -- scroll in that direction */
9851 if (jx != old_jx) /* player has moved horizontally */
9852 scroll_x += (jx - old_jx);
9853 else /* player has moved vertically */
9854 scroll_y += (jy - old_jy);
9858 if (jx != old_jx) /* player has moved horizontally */
9860 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9861 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9862 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9864 /* don't scroll over playfield boundaries */
9865 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9866 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9868 /* don't scroll more than one field at a time */
9869 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9871 /* don't scroll against the player's moving direction */
9872 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9873 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9874 scroll_x = old_scroll_x;
9876 else /* player has moved vertically */
9878 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9879 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9880 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9882 /* don't scroll over playfield boundaries */
9883 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9884 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9886 /* don't scroll more than one field at a time */
9887 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9889 /* don't scroll against the player's moving direction */
9890 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9891 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9892 scroll_y = old_scroll_y;
9896 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9899 if (!options.network && game.centered_player_nr == -1 &&
9900 !AllPlayersInVisibleScreen())
9902 scroll_x = old_scroll_x;
9903 scroll_y = old_scroll_y;
9907 if (!options.network && !AllPlayersInVisibleScreen())
9909 scroll_x = old_scroll_x;
9910 scroll_y = old_scroll_y;
9915 ScrollScreen(player, SCROLL_INIT);
9916 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9921 player->StepFrame = 0;
9923 if (moved & MP_MOVING)
9925 if (old_jx != jx && old_jy == jy)
9926 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9927 else if (old_jx == jx && old_jy != jy)
9928 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9930 DrawLevelField(jx, jy); /* for "crumbled sand" */
9932 player->last_move_dir = player->MovDir;
9933 player->is_moving = TRUE;
9934 player->is_snapping = FALSE;
9935 player->is_switching = FALSE;
9936 player->is_dropping = FALSE;
9940 CheckGravityMovementWhenNotMoving(player);
9942 player->is_moving = FALSE;
9944 /* at this point, the player is allowed to move, but cannot move right now
9945 (e.g. because of something blocking the way) -- ensure that the player
9946 is also allowed to move in the next frame (in old versions before 3.1.1,
9947 the player was forced to wait again for eight frames before next try) */
9949 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9950 player->move_delay = 0; /* allow direct movement in the next frame */
9953 if (player->move_delay == -1) /* not yet initialized by DigField() */
9954 player->move_delay = player->move_delay_value;
9956 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9958 TestIfPlayerTouchesBadThing(jx, jy);
9959 TestIfPlayerTouchesCustomElement(jx, jy);
9962 if (!player->active)
9963 RemovePlayer(player);
9968 void ScrollPlayer(struct PlayerInfo *player, int mode)
9970 int jx = player->jx, jy = player->jy;
9971 int last_jx = player->last_jx, last_jy = player->last_jy;
9972 int move_stepsize = TILEX / player->move_delay_value;
9974 #if USE_NEW_PLAYER_SPEED
9975 if (!player->active)
9978 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9981 if (!player->active || player->MovPos == 0)
9985 if (mode == SCROLL_INIT)
9987 player->actual_frame_counter = FrameCounter;
9988 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9990 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9991 Feld[last_jx][last_jy] == EL_EMPTY)
9993 int last_field_block_delay = 0; /* start with no blocking at all */
9994 int block_delay_adjustment = player->block_delay_adjustment;
9996 /* if player blocks last field, add delay for exactly one move */
9997 if (player->block_last_field)
9999 last_field_block_delay += player->move_delay_value;
10001 /* when blocking enabled, prevent moving up despite gravity */
10002 if (game.gravity && player->MovDir == MV_UP)
10003 block_delay_adjustment = -1;
10006 /* add block delay adjustment (also possible when not blocking) */
10007 last_field_block_delay += block_delay_adjustment;
10009 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10010 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10013 #if USE_NEW_PLAYER_SPEED
10014 if (player->MovPos != 0) /* player has not yet reached destination */
10020 else if (!FrameReached(&player->actual_frame_counter, 1))
10024 printf("::: player->MovPos: %d -> %d\n",
10026 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10029 #if USE_NEW_PLAYER_SPEED
10030 if (player->MovPos != 0)
10032 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10033 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10035 /* before DrawPlayer() to draw correct player graphic for this case */
10036 if (player->MovPos == 0)
10037 CheckGravityMovement(player);
10040 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10041 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10043 /* before DrawPlayer() to draw correct player graphic for this case */
10044 if (player->MovPos == 0)
10045 CheckGravityMovement(player);
10048 if (player->MovPos == 0) /* player reached destination field */
10051 printf("::: player reached destination field\n");
10054 if (player->move_delay_reset_counter > 0)
10056 player->move_delay_reset_counter--;
10058 if (player->move_delay_reset_counter == 0)
10060 /* continue with normal speed after quickly moving through gate */
10061 HALVE_PLAYER_SPEED(player);
10063 /* be able to make the next move without delay */
10064 player->move_delay = 0;
10068 player->last_jx = jx;
10069 player->last_jy = jy;
10071 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10072 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10073 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10075 DrawPlayer(player); /* needed here only to cleanup last field */
10076 RemovePlayer(player);
10078 if (local_player->friends_still_needed == 0 ||
10079 IS_SP_ELEMENT(Feld[jx][jy]))
10080 player->LevelSolved = player->GameOver = TRUE;
10083 /* this breaks one level: "machine", level 000 */
10085 int move_direction = player->MovDir;
10086 int enter_side = MV_DIR_OPPOSITE(move_direction);
10087 int leave_side = move_direction;
10088 int old_jx = last_jx;
10089 int old_jy = last_jy;
10090 int old_element = Feld[old_jx][old_jy];
10091 int new_element = Feld[jx][jy];
10093 if (IS_CUSTOM_ELEMENT(old_element))
10094 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10096 player->index_bit, leave_side);
10098 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10099 CE_PLAYER_LEAVES_X,
10100 player->index_bit, leave_side);
10102 if (IS_CUSTOM_ELEMENT(new_element))
10103 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10104 player->index_bit, enter_side);
10106 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10107 CE_PLAYER_ENTERS_X,
10108 player->index_bit, enter_side);
10110 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10111 CE_MOVE_OF_X, move_direction);
10114 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10116 TestIfPlayerTouchesBadThing(jx, jy);
10117 TestIfPlayerTouchesCustomElement(jx, jy);
10119 /* needed because pushed element has not yet reached its destination,
10120 so it would trigger a change event at its previous field location */
10121 if (!player->is_pushing)
10122 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10124 if (!player->active)
10125 RemovePlayer(player);
10128 if (level.use_step_counter)
10138 if (TimeLeft <= 10 && setup.time_limit)
10139 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10141 DrawGameValue_Time(TimeLeft);
10143 if (!TimeLeft && setup.time_limit)
10144 for (i = 0; i < MAX_PLAYERS; i++)
10145 KillPlayer(&stored_player[i]);
10147 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10148 DrawGameValue_Time(TimePlayed);
10151 if (tape.single_step && tape.recording && !tape.pausing &&
10152 !player->programmed_action)
10153 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10157 void ScrollScreen(struct PlayerInfo *player, int mode)
10159 static unsigned long screen_frame_counter = 0;
10161 if (mode == SCROLL_INIT)
10163 /* set scrolling step size according to actual player's moving speed */
10164 ScrollStepSize = TILEX / player->move_delay_value;
10166 screen_frame_counter = FrameCounter;
10167 ScreenMovDir = player->MovDir;
10168 ScreenMovPos = player->MovPos;
10169 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10172 else if (!FrameReached(&screen_frame_counter, 1))
10177 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10178 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10179 redraw_mask |= REDRAW_FIELD;
10182 ScreenMovDir = MV_NONE;
10185 void TestIfPlayerTouchesCustomElement(int x, int y)
10187 static int xy[4][2] =
10194 static int trigger_sides[4][2] =
10196 /* center side border side */
10197 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10198 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10199 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10200 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10202 static int touch_dir[4] =
10204 MV_LEFT | MV_RIGHT,
10209 int center_element = Feld[x][y]; /* should always be non-moving! */
10212 for (i = 0; i < NUM_DIRECTIONS; i++)
10214 int xx = x + xy[i][0];
10215 int yy = y + xy[i][1];
10216 int center_side = trigger_sides[i][0];
10217 int border_side = trigger_sides[i][1];
10218 int border_element;
10220 if (!IN_LEV_FIELD(xx, yy))
10223 if (IS_PLAYER(x, y))
10225 struct PlayerInfo *player = PLAYERINFO(x, y);
10227 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10228 border_element = Feld[xx][yy]; /* may be moving! */
10229 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10230 border_element = Feld[xx][yy];
10231 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10232 border_element = MovingOrBlocked2Element(xx, yy);
10234 continue; /* center and border element do not touch */
10236 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10237 player->index_bit, border_side);
10238 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10239 CE_PLAYER_TOUCHES_X,
10240 player->index_bit, border_side);
10242 else if (IS_PLAYER(xx, yy))
10244 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10246 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10248 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10249 continue; /* center and border element do not touch */
10252 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10253 player->index_bit, center_side);
10254 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10255 CE_PLAYER_TOUCHES_X,
10256 player->index_bit, center_side);
10262 #if USE_ELEMENT_TOUCHING_BUGFIX
10264 void TestIfElementTouchesCustomElement(int x, int y)
10266 static int xy[4][2] =
10273 static int trigger_sides[4][2] =
10275 /* center side border side */
10276 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10277 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10278 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10279 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10281 static int touch_dir[4] =
10283 MV_LEFT | MV_RIGHT,
10288 boolean change_center_element = FALSE;
10289 int center_element = Feld[x][y]; /* should always be non-moving! */
10290 int border_element_old[NUM_DIRECTIONS];
10293 for (i = 0; i < NUM_DIRECTIONS; i++)
10295 int xx = x + xy[i][0];
10296 int yy = y + xy[i][1];
10297 int border_element;
10299 border_element_old[i] = -1;
10301 if (!IN_LEV_FIELD(xx, yy))
10304 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10305 border_element = Feld[xx][yy]; /* may be moving! */
10306 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10307 border_element = Feld[xx][yy];
10308 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10309 border_element = MovingOrBlocked2Element(xx, yy);
10311 continue; /* center and border element do not touch */
10313 border_element_old[i] = border_element;
10316 for (i = 0; i < NUM_DIRECTIONS; i++)
10318 int xx = x + xy[i][0];
10319 int yy = y + xy[i][1];
10320 int center_side = trigger_sides[i][0];
10321 int border_element = border_element_old[i];
10323 if (border_element == -1)
10326 /* check for change of border element */
10327 CheckElementChangeBySide(xx, yy, border_element, center_element,
10328 CE_TOUCHING_X, center_side);
10331 for (i = 0; i < NUM_DIRECTIONS; i++)
10333 int border_side = trigger_sides[i][1];
10334 int border_element = border_element_old[i];
10336 if (border_element == -1)
10339 /* check for change of center element (but change it only once) */
10340 if (!change_center_element)
10341 change_center_element =
10342 CheckElementChangeBySide(x, y, center_element, border_element,
10343 CE_TOUCHING_X, border_side);
10349 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10351 static int xy[4][2] =
10358 static int trigger_sides[4][2] =
10360 /* center side border side */
10361 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10362 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10363 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10364 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10366 static int touch_dir[4] =
10368 MV_LEFT | MV_RIGHT,
10373 boolean change_center_element = FALSE;
10374 int center_element = Feld[x][y]; /* should always be non-moving! */
10377 for (i = 0; i < NUM_DIRECTIONS; i++)
10379 int xx = x + xy[i][0];
10380 int yy = y + xy[i][1];
10381 int center_side = trigger_sides[i][0];
10382 int border_side = trigger_sides[i][1];
10383 int border_element;
10385 if (!IN_LEV_FIELD(xx, yy))
10388 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10389 border_element = Feld[xx][yy]; /* may be moving! */
10390 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10391 border_element = Feld[xx][yy];
10392 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10393 border_element = MovingOrBlocked2Element(xx, yy);
10395 continue; /* center and border element do not touch */
10397 /* check for change of center element (but change it only once) */
10398 if (!change_center_element)
10399 change_center_element =
10400 CheckElementChangeBySide(x, y, center_element, border_element,
10401 CE_TOUCHING_X, border_side);
10403 /* check for change of border element */
10404 CheckElementChangeBySide(xx, yy, border_element, center_element,
10405 CE_TOUCHING_X, center_side);
10411 void TestIfElementHitsCustomElement(int x, int y, int direction)
10413 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10414 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10415 int hitx = x + dx, hity = y + dy;
10416 int hitting_element = Feld[x][y];
10417 int touched_element;
10419 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10422 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10423 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10425 if (IN_LEV_FIELD(hitx, hity))
10427 int opposite_direction = MV_DIR_OPPOSITE(direction);
10428 int hitting_side = direction;
10429 int touched_side = opposite_direction;
10430 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10431 MovDir[hitx][hity] != direction ||
10432 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10438 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10439 CE_HITTING_X, touched_side);
10441 CheckElementChangeBySide(hitx, hity, touched_element,
10442 hitting_element, CE_HIT_BY_X, hitting_side);
10444 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10445 CE_HIT_BY_SOMETHING, opposite_direction);
10449 /* "hitting something" is also true when hitting the playfield border */
10450 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10451 CE_HITTING_SOMETHING, direction);
10455 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10457 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10458 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10459 int hitx = x + dx, hity = y + dy;
10460 int hitting_element = Feld[x][y];
10461 int touched_element;
10463 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10464 !IS_FREE(hitx, hity) &&
10465 (!IS_MOVING(hitx, hity) ||
10466 MovDir[hitx][hity] != direction ||
10467 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10470 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10474 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10478 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10479 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10481 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10482 EP_CAN_SMASH_EVERYTHING, direction);
10484 if (IN_LEV_FIELD(hitx, hity))
10486 int opposite_direction = MV_DIR_OPPOSITE(direction);
10487 int hitting_side = direction;
10488 int touched_side = opposite_direction;
10490 int touched_element = MovingOrBlocked2Element(hitx, hity);
10493 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10494 MovDir[hitx][hity] != direction ||
10495 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10504 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10505 CE_SMASHED_BY_SOMETHING, opposite_direction);
10507 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10508 CE_OTHER_IS_SMASHING, touched_side);
10510 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10511 CE_OTHER_GETS_SMASHED, hitting_side);
10517 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10519 int i, kill_x = -1, kill_y = -1;
10521 int bad_element = -1;
10522 static int test_xy[4][2] =
10529 static int test_dir[4] =
10537 for (i = 0; i < NUM_DIRECTIONS; i++)
10539 int test_x, test_y, test_move_dir, test_element;
10541 test_x = good_x + test_xy[i][0];
10542 test_y = good_y + test_xy[i][1];
10544 if (!IN_LEV_FIELD(test_x, test_y))
10548 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10550 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10552 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10553 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10555 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10556 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10560 bad_element = test_element;
10566 if (kill_x != -1 || kill_y != -1)
10568 if (IS_PLAYER(good_x, good_y))
10570 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10572 if (player->shield_deadly_time_left > 0 &&
10573 !IS_INDESTRUCTIBLE(bad_element))
10574 Bang(kill_x, kill_y);
10575 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10576 KillPlayer(player);
10579 Bang(good_x, good_y);
10583 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10585 int i, kill_x = -1, kill_y = -1;
10586 int bad_element = Feld[bad_x][bad_y];
10587 static int test_xy[4][2] =
10594 static int touch_dir[4] =
10596 MV_LEFT | MV_RIGHT,
10601 static int test_dir[4] =
10609 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10612 for (i = 0; i < NUM_DIRECTIONS; i++)
10614 int test_x, test_y, test_move_dir, test_element;
10616 test_x = bad_x + test_xy[i][0];
10617 test_y = bad_y + test_xy[i][1];
10618 if (!IN_LEV_FIELD(test_x, test_y))
10622 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10624 test_element = Feld[test_x][test_y];
10626 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10627 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10629 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10630 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10632 /* good thing is player or penguin that does not move away */
10633 if (IS_PLAYER(test_x, test_y))
10635 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10637 if (bad_element == EL_ROBOT && player->is_moving)
10638 continue; /* robot does not kill player if he is moving */
10640 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10642 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10643 continue; /* center and border element do not touch */
10650 else if (test_element == EL_PENGUIN)
10659 if (kill_x != -1 || kill_y != -1)
10661 if (IS_PLAYER(kill_x, kill_y))
10663 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10665 if (player->shield_deadly_time_left > 0 &&
10666 !IS_INDESTRUCTIBLE(bad_element))
10667 Bang(bad_x, bad_y);
10668 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10669 KillPlayer(player);
10672 Bang(kill_x, kill_y);
10676 void TestIfPlayerTouchesBadThing(int x, int y)
10678 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10681 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10683 TestIfGoodThingHitsBadThing(x, y, move_dir);
10686 void TestIfBadThingTouchesPlayer(int x, int y)
10688 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10691 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10693 TestIfBadThingHitsGoodThing(x, y, move_dir);
10696 void TestIfFriendTouchesBadThing(int x, int y)
10698 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10701 void TestIfBadThingTouchesFriend(int x, int y)
10703 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10706 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10708 int i, kill_x = bad_x, kill_y = bad_y;
10709 static int xy[4][2] =
10717 for (i = 0; i < NUM_DIRECTIONS; i++)
10721 x = bad_x + xy[i][0];
10722 y = bad_y + xy[i][1];
10723 if (!IN_LEV_FIELD(x, y))
10726 element = Feld[x][y];
10727 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10728 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10736 if (kill_x != bad_x || kill_y != bad_y)
10737 Bang(bad_x, bad_y);
10740 void KillPlayer(struct PlayerInfo *player)
10742 int jx = player->jx, jy = player->jy;
10744 if (!player->active)
10747 /* remove accessible field at the player's position */
10748 Feld[jx][jy] = EL_EMPTY;
10750 /* deactivate shield (else Bang()/Explode() would not work right) */
10751 player->shield_normal_time_left = 0;
10752 player->shield_deadly_time_left = 0;
10755 BuryPlayer(player);
10758 static void KillPlayerUnlessEnemyProtected(int x, int y)
10760 if (!PLAYER_ENEMY_PROTECTED(x, y))
10761 KillPlayer(PLAYERINFO(x, y));
10764 static void KillPlayerUnlessExplosionProtected(int x, int y)
10766 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10767 KillPlayer(PLAYERINFO(x, y));
10770 void BuryPlayer(struct PlayerInfo *player)
10772 int jx = player->jx, jy = player->jy;
10774 if (!player->active)
10777 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10778 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10780 player->GameOver = TRUE;
10781 RemovePlayer(player);
10784 void RemovePlayer(struct PlayerInfo *player)
10786 int jx = player->jx, jy = player->jy;
10787 int i, found = FALSE;
10789 player->present = FALSE;
10790 player->active = FALSE;
10792 if (!ExplodeField[jx][jy])
10793 StorePlayer[jx][jy] = 0;
10795 if (player->is_moving)
10796 DrawLevelField(player->last_jx, player->last_jy);
10798 for (i = 0; i < MAX_PLAYERS; i++)
10799 if (stored_player[i].active)
10803 AllPlayersGone = TRUE;
10809 #if USE_NEW_SNAP_DELAY
10810 static void setFieldForSnapping(int x, int y, int element, int direction)
10812 struct ElementInfo *ei = &element_info[element];
10813 int direction_bit = MV_DIR_TO_BIT(direction);
10814 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10815 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10816 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10818 Feld[x][y] = EL_ELEMENT_SNAPPING;
10819 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10821 ResetGfxAnimation(x, y);
10823 GfxElement[x][y] = element;
10824 GfxAction[x][y] = action;
10825 GfxDir[x][y] = direction;
10826 GfxFrame[x][y] = -1;
10831 =============================================================================
10832 checkDiagonalPushing()
10833 -----------------------------------------------------------------------------
10834 check if diagonal input device direction results in pushing of object
10835 (by checking if the alternative direction is walkable, diggable, ...)
10836 =============================================================================
10839 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10840 int x, int y, int real_dx, int real_dy)
10842 int jx, jy, dx, dy, xx, yy;
10844 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10847 /* diagonal direction: check alternative direction */
10852 xx = jx + (dx == 0 ? real_dx : 0);
10853 yy = jy + (dy == 0 ? real_dy : 0);
10855 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10859 =============================================================================
10861 -----------------------------------------------------------------------------
10862 x, y: field next to player (non-diagonal) to try to dig to
10863 real_dx, real_dy: direction as read from input device (can be diagonal)
10864 =============================================================================
10867 int DigField(struct PlayerInfo *player,
10868 int oldx, int oldy, int x, int y,
10869 int real_dx, int real_dy, int mode)
10871 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10872 boolean player_was_pushing = player->is_pushing;
10873 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10874 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10875 int jx = oldx, jy = oldy;
10876 int dx = x - jx, dy = y - jy;
10877 int nextx = x + dx, nexty = y + dy;
10878 int move_direction = (dx == -1 ? MV_LEFT :
10879 dx == +1 ? MV_RIGHT :
10881 dy == +1 ? MV_DOWN : MV_NONE);
10882 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10883 int dig_side = MV_DIR_OPPOSITE(move_direction);
10884 int old_element = Feld[jx][jy];
10885 #if USE_FIXED_DONT_RUN_INTO
10886 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10892 if (is_player) /* function can also be called by EL_PENGUIN */
10894 if (player->MovPos == 0)
10896 player->is_digging = FALSE;
10897 player->is_collecting = FALSE;
10900 if (player->MovPos == 0) /* last pushing move finished */
10901 player->is_pushing = FALSE;
10903 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10905 player->is_switching = FALSE;
10906 player->push_delay = -1;
10908 return MP_NO_ACTION;
10912 #if !USE_FIXED_DONT_RUN_INTO
10913 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10914 return MP_NO_ACTION;
10917 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10918 old_element = Back[jx][jy];
10920 /* in case of element dropped at player position, check background */
10921 else if (Back[jx][jy] != EL_EMPTY &&
10922 game.engine_version >= VERSION_IDENT(2,2,0,0))
10923 old_element = Back[jx][jy];
10926 #if USE_FIXED_DONT_RUN_INTO
10927 if (player_can_move && DONT_RUN_INTO(element))
10929 if (element == EL_ACID && dx == 0 && dy == 1)
10932 Feld[jx][jy] = EL_PLAYER_1;
10933 InitMovingField(jx, jy, MV_DOWN);
10934 Store[jx][jy] = EL_ACID;
10935 ContinueMoving(jx, jy);
10936 BuryPlayer(player);
10939 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10941 return MP_DONT_RUN_INTO;
10947 #if USE_FIXED_DONT_RUN_INTO
10948 if (player_can_move && DONT_RUN_INTO(element))
10950 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10952 return MP_DONT_RUN_INTO;
10957 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10958 return MP_NO_ACTION; /* field has no opening in this direction */
10960 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10961 return MP_NO_ACTION; /* field has no opening in this direction */
10964 #if USE_FIXED_DONT_RUN_INTO
10965 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10968 Feld[jx][jy] = EL_PLAYER_1;
10969 InitMovingField(jx, jy, MV_DOWN);
10970 Store[jx][jy] = EL_ACID;
10971 ContinueMoving(jx, jy);
10972 BuryPlayer(player);
10974 return MP_DONT_RUN_INTO;
10980 #if USE_FIXED_DONT_RUN_INTO
10981 if (player_can_move && DONT_RUN_INTO(element))
10983 if (element == EL_ACID && dx == 0 && dy == 1)
10986 Feld[jx][jy] = EL_PLAYER_1;
10987 InitMovingField(jx, jy, MV_DOWN);
10988 Store[jx][jy] = EL_ACID;
10989 ContinueMoving(jx, jy);
10990 BuryPlayer(player);
10993 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10995 return MP_DONT_RUN_INTO;
11000 #if USE_FIXED_DONT_RUN_INTO
11001 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11002 return MP_NO_ACTION;
11005 #if !USE_FIXED_DONT_RUN_INTO
11006 element = Feld[x][y];
11009 collect_count = element_info[element].collect_count_initial;
11011 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11012 return MP_NO_ACTION;
11014 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11015 player_can_move = player_can_move_or_snap;
11017 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11018 game.engine_version >= VERSION_IDENT(2,2,0,0))
11020 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11021 player->index_bit, dig_side);
11022 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11023 player->index_bit, dig_side);
11025 if (Feld[x][y] != element) /* field changed by snapping */
11028 return MP_NO_ACTION;
11031 if (game.gravity && is_player && !player->is_auto_moving &&
11032 canFallDown(player) && move_direction != MV_DOWN &&
11033 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11034 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11036 if (player_can_move &&
11037 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11039 int sound_element = SND_ELEMENT(element);
11040 int sound_action = ACTION_WALKING;
11042 if (IS_RND_GATE(element))
11044 if (!player->key[RND_GATE_NR(element)])
11045 return MP_NO_ACTION;
11047 else if (IS_RND_GATE_GRAY(element))
11049 if (!player->key[RND_GATE_GRAY_NR(element)])
11050 return MP_NO_ACTION;
11052 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11054 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11055 return MP_NO_ACTION;
11057 else if (element == EL_EXIT_OPEN ||
11058 element == EL_SP_EXIT_OPEN ||
11059 element == EL_SP_EXIT_OPENING)
11061 sound_action = ACTION_PASSING; /* player is passing exit */
11063 else if (element == EL_EMPTY)
11065 sound_action = ACTION_MOVING; /* nothing to walk on */
11068 /* play sound from background or player, whatever is available */
11069 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11070 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11072 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11074 else if (player_can_move &&
11075 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11077 if (!ACCESS_FROM(element, opposite_direction))
11078 return MP_NO_ACTION; /* field not accessible from this direction */
11080 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11081 return MP_NO_ACTION;
11083 if (IS_EM_GATE(element))
11085 if (!player->key[EM_GATE_NR(element)])
11086 return MP_NO_ACTION;
11088 else if (IS_EM_GATE_GRAY(element))
11090 if (!player->key[EM_GATE_GRAY_NR(element)])
11091 return MP_NO_ACTION;
11093 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11095 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11096 return MP_NO_ACTION;
11098 else if (IS_SP_PORT(element))
11100 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11101 element == EL_SP_GRAVITY_PORT_RIGHT ||
11102 element == EL_SP_GRAVITY_PORT_UP ||
11103 element == EL_SP_GRAVITY_PORT_DOWN)
11104 game.gravity = !game.gravity;
11105 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11106 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11107 element == EL_SP_GRAVITY_ON_PORT_UP ||
11108 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11109 game.gravity = TRUE;
11110 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11111 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11112 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11113 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11114 game.gravity = FALSE;
11117 /* automatically move to the next field with double speed */
11118 player->programmed_action = move_direction;
11120 if (player->move_delay_reset_counter == 0)
11122 player->move_delay_reset_counter = 2; /* two double speed steps */
11124 DOUBLE_PLAYER_SPEED(player);
11127 PlayLevelSoundAction(x, y, ACTION_PASSING);
11129 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11133 if (mode != DF_SNAP)
11135 GfxElement[x][y] = GFX_ELEMENT(element);
11136 player->is_digging = TRUE;
11139 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11141 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11142 player->index_bit, dig_side);
11144 if (mode == DF_SNAP)
11146 #if USE_NEW_SNAP_DELAY
11147 if (level.block_snap_field)
11148 setFieldForSnapping(x, y, element, move_direction);
11150 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11152 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11155 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11156 player->index_bit, dig_side);
11159 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11163 if (is_player && mode != DF_SNAP)
11165 GfxElement[x][y] = element;
11166 player->is_collecting = TRUE;
11169 if (element == EL_SPEED_PILL)
11171 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11173 else if (element == EL_EXTRA_TIME && level.time > 0)
11175 TimeLeft += level.extra_time;
11176 DrawGameValue_Time(TimeLeft);
11178 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11180 player->shield_normal_time_left += level.shield_normal_time;
11181 if (element == EL_SHIELD_DEADLY)
11182 player->shield_deadly_time_left += level.shield_deadly_time;
11184 else if (element == EL_DYNAMITE ||
11185 element == EL_EM_DYNAMITE ||
11186 element == EL_SP_DISK_RED)
11188 if (player->inventory_size < MAX_INVENTORY_SIZE)
11189 player->inventory_element[player->inventory_size++] = element;
11191 DrawGameValue_Dynamite(local_player->inventory_size);
11193 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11195 player->dynabomb_count++;
11196 player->dynabombs_left++;
11198 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11200 player->dynabomb_size++;
11202 else if (element == EL_DYNABOMB_INCREASE_POWER)
11204 player->dynabomb_xl = TRUE;
11206 else if (IS_KEY(element))
11208 player->key[KEY_NR(element)] = TRUE;
11210 DrawGameValue_Keys(player->key);
11212 redraw_mask |= REDRAW_DOOR_1;
11214 else if (IS_ENVELOPE(element))
11216 player->show_envelope = element;
11218 else if (element == EL_EMC_LENSES)
11220 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11222 RedrawAllInvisibleElementsForLenses();
11224 else if (element == EL_EMC_MAGNIFIER)
11226 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11228 RedrawAllInvisibleElementsForMagnifier();
11230 else if (IS_DROPPABLE(element) ||
11231 IS_THROWABLE(element)) /* can be collected and dropped */
11235 if (collect_count == 0)
11236 player->inventory_infinite_element = element;
11238 for (i = 0; i < collect_count; i++)
11239 if (player->inventory_size < MAX_INVENTORY_SIZE)
11240 player->inventory_element[player->inventory_size++] = element;
11242 DrawGameValue_Dynamite(local_player->inventory_size);
11244 else if (collect_count > 0)
11246 local_player->gems_still_needed -= collect_count;
11247 if (local_player->gems_still_needed < 0)
11248 local_player->gems_still_needed = 0;
11250 DrawGameValue_Emeralds(local_player->gems_still_needed);
11253 RaiseScoreElement(element);
11254 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11257 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11258 player->index_bit, dig_side);
11260 if (mode == DF_SNAP)
11262 #if USE_NEW_SNAP_DELAY
11263 if (level.block_snap_field)
11264 setFieldForSnapping(x, y, element, move_direction);
11266 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11268 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11271 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11272 player->index_bit, dig_side);
11275 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11277 if (mode == DF_SNAP && element != EL_BD_ROCK)
11278 return MP_NO_ACTION;
11280 if (CAN_FALL(element) && dy)
11281 return MP_NO_ACTION;
11283 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11284 !(element == EL_SPRING && level.use_spring_bug))
11285 return MP_NO_ACTION;
11287 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11288 ((move_direction & MV_VERTICAL &&
11289 ((element_info[element].move_pattern & MV_LEFT &&
11290 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11291 (element_info[element].move_pattern & MV_RIGHT &&
11292 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11293 (move_direction & MV_HORIZONTAL &&
11294 ((element_info[element].move_pattern & MV_UP &&
11295 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11296 (element_info[element].move_pattern & MV_DOWN &&
11297 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11298 return MP_NO_ACTION;
11300 /* do not push elements already moving away faster than player */
11301 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11302 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11303 return MP_NO_ACTION;
11305 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11307 if (player->push_delay_value == -1 || !player_was_pushing)
11308 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11310 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11312 if (player->push_delay_value == -1)
11313 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11315 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11317 if (!player->is_pushing)
11318 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11321 player->is_pushing = TRUE;
11323 if (!(IN_LEV_FIELD(nextx, nexty) &&
11324 (IS_FREE(nextx, nexty) ||
11325 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11326 IS_SB_ELEMENT(element)))))
11327 return MP_NO_ACTION;
11329 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11330 return MP_NO_ACTION;
11332 if (player->push_delay == -1) /* new pushing; restart delay */
11333 player->push_delay = 0;
11335 if (player->push_delay < player->push_delay_value &&
11336 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11337 element != EL_SPRING && element != EL_BALLOON)
11339 /* make sure that there is no move delay before next try to push */
11340 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11341 player->move_delay = 0;
11343 return MP_NO_ACTION;
11346 if (IS_SB_ELEMENT(element))
11348 if (element == EL_SOKOBAN_FIELD_FULL)
11350 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11351 local_player->sokobanfields_still_needed++;
11354 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11356 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11357 local_player->sokobanfields_still_needed--;
11360 Feld[x][y] = EL_SOKOBAN_OBJECT;
11362 if (Back[x][y] == Back[nextx][nexty])
11363 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11364 else if (Back[x][y] != 0)
11365 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11368 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11371 if (local_player->sokobanfields_still_needed == 0 &&
11372 game.emulation == EMU_SOKOBAN)
11374 player->LevelSolved = player->GameOver = TRUE;
11375 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11379 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11381 InitMovingField(x, y, move_direction);
11382 GfxAction[x][y] = ACTION_PUSHING;
11384 if (mode == DF_SNAP)
11385 ContinueMoving(x, y);
11387 MovPos[x][y] = (dx != 0 ? dx : dy);
11389 Pushed[x][y] = TRUE;
11390 Pushed[nextx][nexty] = TRUE;
11392 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11393 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11395 player->push_delay_value = -1; /* get new value later */
11397 /* check for element change _after_ element has been pushed */
11398 if (game.use_change_when_pushing_bug)
11400 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11401 player->index_bit, dig_side);
11402 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11403 player->index_bit, dig_side);
11406 else if (IS_SWITCHABLE(element))
11408 if (PLAYER_SWITCHING(player, x, y))
11410 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11411 player->index_bit, dig_side);
11416 player->is_switching = TRUE;
11417 player->switch_x = x;
11418 player->switch_y = y;
11420 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11422 if (element == EL_ROBOT_WHEEL)
11424 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11428 DrawLevelField(x, y);
11430 else if (element == EL_SP_TERMINAL)
11435 SCAN_PLAYFIELD(xx, yy)
11437 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11440 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11442 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11443 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11446 else if (IS_BELT_SWITCH(element))
11448 ToggleBeltSwitch(x, y);
11450 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11451 element == EL_SWITCHGATE_SWITCH_DOWN)
11453 ToggleSwitchgateSwitch(x, y);
11455 else if (element == EL_LIGHT_SWITCH ||
11456 element == EL_LIGHT_SWITCH_ACTIVE)
11458 ToggleLightSwitch(x, y);
11460 else if (element == EL_TIMEGATE_SWITCH)
11462 ActivateTimegateSwitch(x, y);
11464 else if (element == EL_BALLOON_SWITCH_LEFT ||
11465 element == EL_BALLOON_SWITCH_RIGHT ||
11466 element == EL_BALLOON_SWITCH_UP ||
11467 element == EL_BALLOON_SWITCH_DOWN ||
11468 element == EL_BALLOON_SWITCH_NONE ||
11469 element == EL_BALLOON_SWITCH_ANY)
11471 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11472 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11473 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11474 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11475 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11478 else if (element == EL_LAMP)
11480 Feld[x][y] = EL_LAMP_ACTIVE;
11481 local_player->lights_still_needed--;
11483 ResetGfxAnimation(x, y);
11484 DrawLevelField(x, y);
11486 else if (element == EL_TIME_ORB_FULL)
11488 Feld[x][y] = EL_TIME_ORB_EMPTY;
11490 if (level.time > 0 || level.use_time_orb_bug)
11492 TimeLeft += level.time_orb_time;
11493 DrawGameValue_Time(TimeLeft);
11496 ResetGfxAnimation(x, y);
11497 DrawLevelField(x, y);
11499 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11500 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11504 game.ball_state = !game.ball_state;
11507 SCAN_PLAYFIELD(xx, yy)
11509 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11512 int e = Feld[xx][yy];
11514 if (game.ball_state)
11516 if (e == EL_EMC_MAGIC_BALL)
11517 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11518 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11519 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11523 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11524 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11525 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11526 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11531 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11532 player->index_bit, dig_side);
11534 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11535 player->index_bit, dig_side);
11537 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11538 player->index_bit, dig_side);
11544 if (!PLAYER_SWITCHING(player, x, y))
11546 player->is_switching = TRUE;
11547 player->switch_x = x;
11548 player->switch_y = y;
11550 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11551 player->index_bit, dig_side);
11552 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11553 player->index_bit, dig_side);
11555 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11556 player->index_bit, dig_side);
11557 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11558 player->index_bit, dig_side);
11561 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11562 player->index_bit, dig_side);
11563 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11564 player->index_bit, dig_side);
11566 return MP_NO_ACTION;
11569 player->push_delay = -1;
11571 if (is_player) /* function can also be called by EL_PENGUIN */
11573 if (Feld[x][y] != element) /* really digged/collected something */
11574 player->is_collecting = !player->is_digging;
11580 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11582 int jx = player->jx, jy = player->jy;
11583 int x = jx + dx, y = jy + dy;
11584 int snap_direction = (dx == -1 ? MV_LEFT :
11585 dx == +1 ? MV_RIGHT :
11587 dy == +1 ? MV_DOWN : MV_NONE);
11588 boolean can_continue_snapping = (level.continuous_snapping &&
11589 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11591 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11594 if (!player->active || !IN_LEV_FIELD(x, y))
11602 if (player->MovPos == 0)
11603 player->is_pushing = FALSE;
11605 player->is_snapping = FALSE;
11607 if (player->MovPos == 0)
11609 player->is_moving = FALSE;
11610 player->is_digging = FALSE;
11611 player->is_collecting = FALSE;
11617 #if USE_NEW_CONTINUOUS_SNAPPING
11618 /* prevent snapping with already pressed snap key when not allowed */
11619 if (player->is_snapping && !can_continue_snapping)
11622 if (player->is_snapping)
11626 player->MovDir = snap_direction;
11628 if (player->MovPos == 0)
11630 player->is_moving = FALSE;
11631 player->is_digging = FALSE;
11632 player->is_collecting = FALSE;
11635 player->is_dropping = FALSE;
11637 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11640 player->is_snapping = TRUE;
11642 if (player->MovPos == 0)
11644 player->is_moving = FALSE;
11645 player->is_digging = FALSE;
11646 player->is_collecting = FALSE;
11649 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11650 DrawLevelField(player->last_jx, player->last_jy);
11652 DrawLevelField(x, y);
11657 boolean DropElement(struct PlayerInfo *player)
11659 int old_element, new_element;
11660 int dropx = player->jx, dropy = player->jy;
11661 int drop_direction = player->MovDir;
11662 int drop_side = drop_direction;
11663 int drop_element = (player->inventory_size > 0 ?
11664 player->inventory_element[player->inventory_size - 1] :
11665 player->inventory_infinite_element != EL_UNDEFINED ?
11666 player->inventory_infinite_element :
11667 player->dynabombs_left > 0 ?
11668 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11671 /* do not drop an element on top of another element; when holding drop key
11672 pressed without moving, dropped element must move away before the next
11673 element can be dropped (this is especially important if the next element
11674 is dynamite, which can be placed on background for historical reasons) */
11675 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11678 if (IS_THROWABLE(drop_element))
11680 dropx += GET_DX_FROM_DIR(drop_direction);
11681 dropy += GET_DY_FROM_DIR(drop_direction);
11683 if (!IN_LEV_FIELD(dropx, dropy))
11687 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11688 new_element = drop_element; /* default: no change when dropping */
11690 /* check if player is active, not moving and ready to drop */
11691 if (!player->active || player->MovPos || player->drop_delay > 0)
11694 /* check if player has anything that can be dropped */
11695 if (new_element == EL_UNDEFINED)
11698 /* check if anything can be dropped at the current position */
11699 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11702 /* collected custom elements can only be dropped on empty fields */
11703 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11706 if (old_element != EL_EMPTY)
11707 Back[dropx][dropy] = old_element; /* store old element on this field */
11709 ResetGfxAnimation(dropx, dropy);
11710 ResetRandomAnimationValue(dropx, dropy);
11712 if (player->inventory_size > 0 ||
11713 player->inventory_infinite_element != EL_UNDEFINED)
11715 if (player->inventory_size > 0)
11717 player->inventory_size--;
11719 DrawGameValue_Dynamite(local_player->inventory_size);
11721 if (new_element == EL_DYNAMITE)
11722 new_element = EL_DYNAMITE_ACTIVE;
11723 else if (new_element == EL_EM_DYNAMITE)
11724 new_element = EL_EM_DYNAMITE_ACTIVE;
11725 else if (new_element == EL_SP_DISK_RED)
11726 new_element = EL_SP_DISK_RED_ACTIVE;
11729 Feld[dropx][dropy] = new_element;
11731 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11732 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11733 el2img(Feld[dropx][dropy]), 0);
11735 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11737 /* needed if previous element just changed to "empty" in the last frame */
11738 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11740 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11741 player->index_bit, drop_side);
11742 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11744 player->index_bit, drop_side);
11746 TestIfElementTouchesCustomElement(dropx, dropy);
11748 else /* player is dropping a dyna bomb */
11750 player->dynabombs_left--;
11752 Feld[dropx][dropy] = new_element;
11754 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11755 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11756 el2img(Feld[dropx][dropy]), 0);
11758 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11761 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11762 InitField_WithBug1(dropx, dropy, FALSE);
11764 new_element = Feld[dropx][dropy]; /* element might have changed */
11766 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11767 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11769 int move_direction, nextx, nexty;
11771 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11772 MovDir[dropx][dropy] = drop_direction;
11774 move_direction = MovDir[dropx][dropy];
11775 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11776 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11778 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11779 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
11782 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11783 player->is_dropping = TRUE;
11785 player->drop_x = dropx;
11786 player->drop_y = dropy;
11791 /* ------------------------------------------------------------------------- */
11792 /* game sound playing functions */
11793 /* ------------------------------------------------------------------------- */
11795 static int *loop_sound_frame = NULL;
11796 static int *loop_sound_volume = NULL;
11798 void InitPlayLevelSound()
11800 int num_sounds = getSoundListSize();
11802 checked_free(loop_sound_frame);
11803 checked_free(loop_sound_volume);
11805 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11806 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11809 static void PlayLevelSound(int x, int y, int nr)
11811 int sx = SCREENX(x), sy = SCREENY(y);
11812 int volume, stereo_position;
11813 int max_distance = 8;
11814 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11816 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11817 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11820 if (!IN_LEV_FIELD(x, y) ||
11821 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11822 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11825 volume = SOUND_MAX_VOLUME;
11827 if (!IN_SCR_FIELD(sx, sy))
11829 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11830 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11832 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11835 stereo_position = (SOUND_MAX_LEFT +
11836 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11837 (SCR_FIELDX + 2 * max_distance));
11839 if (IS_LOOP_SOUND(nr))
11841 /* This assures that quieter loop sounds do not overwrite louder ones,
11842 while restarting sound volume comparison with each new game frame. */
11844 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11847 loop_sound_volume[nr] = volume;
11848 loop_sound_frame[nr] = FrameCounter;
11851 PlaySoundExt(nr, volume, stereo_position, type);
11854 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11856 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11857 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11858 y < LEVELY(BY1) ? LEVELY(BY1) :
11859 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11863 static void PlayLevelSoundAction(int x, int y, int action)
11865 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11868 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11870 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11872 if (sound_effect != SND_UNDEFINED)
11873 PlayLevelSound(x, y, sound_effect);
11876 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11879 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11881 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11882 PlayLevelSound(x, y, sound_effect);
11885 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11887 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11889 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11890 PlayLevelSound(x, y, sound_effect);
11893 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11895 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11897 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11898 StopSound(sound_effect);
11901 static void PlayLevelMusic()
11903 if (levelset.music[level_nr] != MUS_UNDEFINED)
11904 PlayMusic(levelset.music[level_nr]); /* from config file */
11906 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11909 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11911 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11916 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11920 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11924 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11928 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11932 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11936 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11940 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11943 case SAMPLE_android_clone:
11944 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11947 case SAMPLE_android_move:
11948 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11951 case SAMPLE_spring:
11952 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11956 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
11960 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11963 case SAMPLE_eater_eat:
11964 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11968 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11971 case SAMPLE_collect:
11972 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11975 case SAMPLE_diamond:
11976 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11979 case SAMPLE_squash:
11980 /* !!! CHECK THIS !!! */
11982 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11984 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11988 case SAMPLE_wonderfall:
11989 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11993 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11997 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12001 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12005 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12009 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12013 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12016 case SAMPLE_wonder:
12017 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12021 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12024 case SAMPLE_exit_open:
12025 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12028 case SAMPLE_exit_leave:
12029 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12032 case SAMPLE_dynamite:
12033 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12037 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12041 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12045 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12049 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12053 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12057 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12061 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12066 void RaiseScore(int value)
12068 local_player->score += value;
12070 DrawGameValue_Score(local_player->score);
12073 void RaiseScoreElement(int element)
12078 case EL_BD_DIAMOND:
12079 case EL_EMERALD_YELLOW:
12080 case EL_EMERALD_RED:
12081 case EL_EMERALD_PURPLE:
12082 case EL_SP_INFOTRON:
12083 RaiseScore(level.score[SC_EMERALD]);
12086 RaiseScore(level.score[SC_DIAMOND]);
12089 RaiseScore(level.score[SC_CRYSTAL]);
12092 RaiseScore(level.score[SC_PEARL]);
12095 case EL_BD_BUTTERFLY:
12096 case EL_SP_ELECTRON:
12097 RaiseScore(level.score[SC_BUG]);
12100 case EL_BD_FIREFLY:
12101 case EL_SP_SNIKSNAK:
12102 RaiseScore(level.score[SC_SPACESHIP]);
12105 case EL_DARK_YAMYAM:
12106 RaiseScore(level.score[SC_YAMYAM]);
12109 RaiseScore(level.score[SC_ROBOT]);
12112 RaiseScore(level.score[SC_PACMAN]);
12115 RaiseScore(level.score[SC_NUT]);
12118 case EL_EM_DYNAMITE:
12119 case EL_SP_DISK_RED:
12120 case EL_DYNABOMB_INCREASE_NUMBER:
12121 case EL_DYNABOMB_INCREASE_SIZE:
12122 case EL_DYNABOMB_INCREASE_POWER:
12123 RaiseScore(level.score[SC_DYNAMITE]);
12125 case EL_SHIELD_NORMAL:
12126 case EL_SHIELD_DEADLY:
12127 RaiseScore(level.score[SC_SHIELD]);
12129 case EL_EXTRA_TIME:
12130 RaiseScore(level.extra_time_score);
12144 RaiseScore(level.score[SC_KEY]);
12147 RaiseScore(element_info[element].collect_score);
12152 void RequestQuitGame(boolean ask_if_really_quit)
12154 if (AllPlayersGone ||
12155 !ask_if_really_quit ||
12156 level_editor_test_game ||
12157 Request("Do you really want to quit the game ?",
12158 REQ_ASK | REQ_STAY_CLOSED))
12160 #if defined(NETWORK_AVALIABLE)
12161 if (options.network)
12162 SendToServer_StopPlaying();
12166 game_status = GAME_MODE_MAIN;
12172 if (tape.playing && tape.deactivate_display)
12173 TapeDeactivateDisplayOff(TRUE);
12175 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12177 if (tape.playing && tape.deactivate_display)
12178 TapeDeactivateDisplayOn();
12183 /* ---------- new game button stuff ---------------------------------------- */
12185 /* graphic position values for game buttons */
12186 #define GAME_BUTTON_XSIZE 30
12187 #define GAME_BUTTON_YSIZE 30
12188 #define GAME_BUTTON_XPOS 5
12189 #define GAME_BUTTON_YPOS 215
12190 #define SOUND_BUTTON_XPOS 5
12191 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12193 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12194 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12195 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12196 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12197 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12198 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12205 } gamebutton_info[NUM_GAME_BUTTONS] =
12208 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12213 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12214 GAME_CTRL_ID_PAUSE,
12218 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12223 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12224 SOUND_CTRL_ID_MUSIC,
12225 "background music on/off"
12228 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12229 SOUND_CTRL_ID_LOOPS,
12230 "sound loops on/off"
12233 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12234 SOUND_CTRL_ID_SIMPLE,
12235 "normal sounds on/off"
12239 void CreateGameButtons()
12243 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12245 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12246 struct GadgetInfo *gi;
12249 unsigned long event_mask;
12250 int gd_xoffset, gd_yoffset;
12251 int gd_x1, gd_x2, gd_y1, gd_y2;
12254 gd_xoffset = gamebutton_info[i].x;
12255 gd_yoffset = gamebutton_info[i].y;
12256 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12257 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12259 if (id == GAME_CTRL_ID_STOP ||
12260 id == GAME_CTRL_ID_PAUSE ||
12261 id == GAME_CTRL_ID_PLAY)
12263 button_type = GD_TYPE_NORMAL_BUTTON;
12265 event_mask = GD_EVENT_RELEASED;
12266 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12267 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12271 button_type = GD_TYPE_CHECK_BUTTON;
12273 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12274 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12275 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12276 event_mask = GD_EVENT_PRESSED;
12277 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12278 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12281 gi = CreateGadget(GDI_CUSTOM_ID, id,
12282 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12283 GDI_X, DX + gd_xoffset,
12284 GDI_Y, DY + gd_yoffset,
12285 GDI_WIDTH, GAME_BUTTON_XSIZE,
12286 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12287 GDI_TYPE, button_type,
12288 GDI_STATE, GD_BUTTON_UNPRESSED,
12289 GDI_CHECKED, checked,
12290 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12291 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12292 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12293 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12294 GDI_EVENT_MASK, event_mask,
12295 GDI_CALLBACK_ACTION, HandleGameButtons,
12299 Error(ERR_EXIT, "cannot create gadget");
12301 game_gadget[id] = gi;
12305 void FreeGameButtons()
12309 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12310 FreeGadget(game_gadget[i]);
12313 static void MapGameButtons()
12317 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12318 MapGadget(game_gadget[i]);
12321 void UnmapGameButtons()
12325 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12326 UnmapGadget(game_gadget[i]);
12329 static void HandleGameButtons(struct GadgetInfo *gi)
12331 int id = gi->custom_id;
12333 if (game_status != GAME_MODE_PLAYING)
12338 case GAME_CTRL_ID_STOP:
12339 RequestQuitGame(TRUE);
12342 case GAME_CTRL_ID_PAUSE:
12343 if (options.network)
12345 #if defined(NETWORK_AVALIABLE)
12347 SendToServer_ContinuePlaying();
12349 SendToServer_PausePlaying();
12353 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12356 case GAME_CTRL_ID_PLAY:
12359 #if defined(NETWORK_AVALIABLE)
12360 if (options.network)
12361 SendToServer_ContinuePlaying();
12365 tape.pausing = FALSE;
12366 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12371 case SOUND_CTRL_ID_MUSIC:
12372 if (setup.sound_music)
12374 setup.sound_music = FALSE;
12377 else if (audio.music_available)
12379 setup.sound = setup.sound_music = TRUE;
12381 SetAudioMode(setup.sound);
12387 case SOUND_CTRL_ID_LOOPS:
12388 if (setup.sound_loops)
12389 setup.sound_loops = FALSE;
12390 else if (audio.loops_available)
12392 setup.sound = setup.sound_loops = TRUE;
12393 SetAudioMode(setup.sound);
12397 case SOUND_CTRL_ID_SIMPLE:
12398 if (setup.sound_simple)
12399 setup.sound_simple = FALSE;
12400 else if (audio.sound_available)
12402 setup.sound = setup.sound_simple = TRUE;
12403 SetAudioMode(setup.sound);