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_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
45 /* for MovePlayer() */
46 #define MF_NO_ACTION 0
50 /* for ScrollPlayer() */
52 #define SCROLL_GO_ON 1
54 /* for Bang()/Explode() */
55 #define EX_PHASE_START 0
56 #define EX_TYPE_NONE 0
57 #define EX_TYPE_NORMAL (1 << 0)
58 #define EX_TYPE_CENTER (1 << 1)
59 #define EX_TYPE_BORDER (1 << 2)
60 #define EX_TYPE_CROSS (1 << 3)
61 #define EX_TYPE_DYNA (1 << 4)
62 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
64 /* special positions in the game control window (relative to control window) */
67 #define XX_EMERALDS 29
68 #define YY_EMERALDS 54
69 #define XX_DYNAMITE 29
70 #define YY_DYNAMITE 89
79 /* special positions in the game control window (relative to main window) */
80 #define DX_LEVEL (DX + XX_LEVEL)
81 #define DY_LEVEL (DY + YY_LEVEL)
82 #define DX_EMERALDS (DX + XX_EMERALDS)
83 #define DY_EMERALDS (DY + YY_EMERALDS)
84 #define DX_DYNAMITE (DX + XX_DYNAMITE)
85 #define DY_DYNAMITE (DY + YY_DYNAMITE)
86 #define DX_KEYS (DX + XX_KEYS)
87 #define DY_KEYS (DY + YY_KEYS)
88 #define DX_SCORE (DX + XX_SCORE)
89 #define DY_SCORE (DY + YY_SCORE)
90 #define DX_TIME1 (DX + XX_TIME1)
91 #define DX_TIME2 (DX + XX_TIME2)
92 #define DY_TIME (DY + YY_TIME)
94 /* values for initial player move delay (initial delay counter value) */
95 #define INITIAL_MOVE_DELAY_OFF -1
96 #define INITIAL_MOVE_DELAY_ON 0
98 /* values for player movement speed (which is in fact a delay value) */
99 #define MOVE_DELAY_MIN_SPEED 32
100 #define MOVE_DELAY_NORMAL_SPEED 8
101 #define MOVE_DELAY_HIGH_SPEED 4
102 #define MOVE_DELAY_MAX_SPEED 1
105 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
106 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
108 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
109 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
111 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
112 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
114 /* values for other actions */
115 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
116 #define MOVE_STEPSIZE_MIN (1)
117 #define MOVE_STEPSIZE_MAX (TILEX)
119 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
120 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
122 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
124 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
125 RND(element_info[e].push_delay_random))
126 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
127 RND(element_info[e].drop_delay_random))
128 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
129 RND(element_info[e].move_delay_random))
130 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
131 (element_info[e].move_delay_random))
132 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
133 RND(element_info[e].ce_value_random_initial))
134 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
135 RND((c)->delay_random * (c)->delay_frames))
136 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
137 RND((c)->delay_random))
139 #define GET_TARGET_ELEMENT(e, ch) \
140 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
141 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
143 #define CAN_GROW_INTO(e) \
144 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
146 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
150 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 (CAN_MOVE_INTO_ACID(e) && \
153 Feld[x][y] == EL_ACID) || \
156 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
157 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
158 (CAN_MOVE_INTO_ACID(e) && \
159 Feld[x][y] == EL_ACID) || \
162 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
163 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
165 (CAN_MOVE_INTO_ACID(e) && \
166 Feld[x][y] == EL_ACID) || \
167 (DONT_COLLIDE_WITH(e) && \
169 !PLAYER_ENEMY_PROTECTED(x, y))))
171 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
172 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
174 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
175 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
177 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
178 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
180 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
183 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
186 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
189 #define PIG_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
192 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
194 IS_FOOD_PENGUIN(Feld[x][y])))
195 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
198 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
201 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
204 #define GROUP_NR(e) ((e) - EL_GROUP_START)
205 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
206 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
207 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
209 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
210 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
212 #define CE_ENTER_FIELD_COND(e, x, y) \
213 (!IS_PLAYER(x, y) && \
214 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
216 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
219 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
220 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
222 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
223 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
224 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
225 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
227 /* game button identifiers */
228 #define GAME_CTRL_ID_STOP 0
229 #define GAME_CTRL_ID_PAUSE 1
230 #define GAME_CTRL_ID_PLAY 2
231 #define SOUND_CTRL_ID_MUSIC 3
232 #define SOUND_CTRL_ID_LOOPS 4
233 #define SOUND_CTRL_ID_SIMPLE 5
235 #define NUM_GAME_BUTTONS 6
238 /* forward declaration for internal use */
240 static void CreateField(int, int, int);
242 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
243 static void AdvanceFrameAndPlayerCounters(int);
245 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
246 static boolean MovePlayer(struct PlayerInfo *, int, int);
247 static void ScrollPlayer(struct PlayerInfo *, int);
248 static void ScrollScreen(struct PlayerInfo *, int);
250 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
252 static void InitBeltMovement(void);
253 static void CloseAllOpenTimegates(void);
254 static void CheckGravityMovement(struct PlayerInfo *);
255 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
256 static void KillPlayerUnlessEnemyProtected(int, int);
257 static void KillPlayerUnlessExplosionProtected(int, int);
259 static void TestIfPlayerTouchesCustomElement(int, int);
260 static void TestIfElementTouchesCustomElement(int, int);
261 static void TestIfElementHitsCustomElement(int, int, int);
263 static void TestIfElementSmashesCustomElement(int, int, int);
266 static void HandleElementChange(int, int, int);
268 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
269 #define CheckTriggeredElementChange(x, y, e, ev) \
270 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
271 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
272 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
273 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
274 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
275 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
276 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
278 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
279 #define CheckElementChange(x, y, e, te, ev) \
280 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
281 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
282 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
283 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
284 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
286 static void PlayLevelSound(int, int, int);
287 static void PlayLevelSoundNearest(int, int, int);
288 static void PlayLevelSoundAction(int, int, int);
289 static void PlayLevelSoundElementAction(int, int, int, int);
290 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
291 static void PlayLevelSoundActionIfLoop(int, int, int);
292 static void StopLevelSoundActionIfLoop(int, int, int);
293 static void PlayLevelMusic();
295 static void MapGameButtons();
296 static void HandleGameButtons(struct GadgetInfo *);
298 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
301 /* ------------------------------------------------------------------------- */
302 /* definition of elements that automatically change to other elements after */
303 /* a specified time, eventually calling a function when changing */
304 /* ------------------------------------------------------------------------- */
306 /* forward declaration for changer functions */
307 static void InitBuggyBase(int, int);
308 static void WarnBuggyBase(int, int);
310 static void InitTrap(int, int);
311 static void ActivateTrap(int, int);
312 static void ChangeActiveTrap(int, int);
314 static void InitRobotWheel(int, int);
315 static void RunRobotWheel(int, int);
316 static void StopRobotWheel(int, int);
318 static void InitTimegateWheel(int, int);
319 static void RunTimegateWheel(int, int);
321 static void InitMagicBallDelay(int, int);
322 static void ActivateMagicBall(int, int);
324 struct ChangingElementInfo
329 void (*pre_change_function)(int x, int y);
330 void (*change_function)(int x, int y);
331 void (*post_change_function)(int x, int y);
334 static struct ChangingElementInfo change_delay_list[] =
385 EL_SWITCHGATE_OPENING,
393 EL_SWITCHGATE_CLOSING,
394 EL_SWITCHGATE_CLOSED,
426 EL_ACID_SPLASH_RIGHT,
435 EL_SP_BUGGY_BASE_ACTIVATING,
442 EL_SP_BUGGY_BASE_ACTIVATING,
443 EL_SP_BUGGY_BASE_ACTIVE,
450 EL_SP_BUGGY_BASE_ACTIVE,
474 EL_ROBOT_WHEEL_ACTIVE,
482 EL_TIMEGATE_SWITCH_ACTIVE,
490 EL_EMC_MAGIC_BALL_ACTIVE,
491 EL_EMC_MAGIC_BALL_ACTIVE,
511 int push_delay_fixed, push_delay_random;
516 { EL_BALLOON, 0, 0 },
518 { EL_SOKOBAN_OBJECT, 2, 0 },
519 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
520 { EL_SATELLITE, 2, 0 },
521 { EL_SP_DISK_YELLOW, 2, 0 },
523 { EL_UNDEFINED, 0, 0 },
531 move_stepsize_list[] =
533 { EL_AMOEBA_DROP, 2 },
534 { EL_AMOEBA_DROPPING, 2 },
535 { EL_QUICKSAND_FILLING, 1 },
536 { EL_QUICKSAND_EMPTYING, 1 },
537 { EL_MAGIC_WALL_FILLING, 2 },
538 { EL_BD_MAGIC_WALL_FILLING, 2 },
539 { EL_MAGIC_WALL_EMPTYING, 2 },
540 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
550 collect_count_list[] =
553 { EL_BD_DIAMOND, 1 },
554 { EL_EMERALD_YELLOW, 1 },
555 { EL_EMERALD_RED, 1 },
556 { EL_EMERALD_PURPLE, 1 },
558 { EL_SP_INFOTRON, 1 },
570 access_direction_list[] =
572 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
573 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
574 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
575 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
576 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
577 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
578 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
579 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
580 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
581 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
582 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
584 { EL_SP_PORT_LEFT, MV_RIGHT },
585 { EL_SP_PORT_RIGHT, MV_LEFT },
586 { EL_SP_PORT_UP, MV_DOWN },
587 { EL_SP_PORT_DOWN, MV_UP },
588 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
589 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
590 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
591 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
592 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
593 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
594 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
595 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
596 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
597 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
598 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
599 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
600 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
601 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
602 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
604 { EL_UNDEFINED, MV_NONE }
607 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
609 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
610 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
611 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
612 IS_JUST_CHANGING(x, y))
614 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
616 /* static variables for playfield scan mode (scanning forward or backward) */
617 static int playfield_scan_start_x = 0;
618 static int playfield_scan_start_y = 0;
619 static int playfield_scan_delta_x = 1;
620 static int playfield_scan_delta_y = 1;
622 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
623 (y) >= 0 && (y) <= lev_fieldy - 1; \
624 (y) += playfield_scan_delta_y) \
625 for ((x) = playfield_scan_start_x; \
626 (x) >= 0 && (x) <= lev_fieldx - 1; \
627 (x) += playfield_scan_delta_x) \
629 static void InitPlayfieldScanModeVars()
631 if (game.use_reverse_scan_direction)
633 playfield_scan_start_x = lev_fieldx - 1;
634 playfield_scan_start_y = lev_fieldy - 1;
636 playfield_scan_delta_x = -1;
637 playfield_scan_delta_y = -1;
641 playfield_scan_start_x = 0;
642 playfield_scan_start_y = 0;
644 playfield_scan_delta_x = 1;
645 playfield_scan_delta_y = 1;
649 static void InitPlayfieldScanMode(int mode)
651 game.use_reverse_scan_direction =
652 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
654 InitPlayfieldScanModeVars();
657 void GetPlayerConfig()
659 if (!audio.sound_available)
660 setup.sound_simple = FALSE;
662 if (!audio.loops_available)
663 setup.sound_loops = FALSE;
665 if (!audio.music_available)
666 setup.sound_music = FALSE;
668 if (!video.fullscreen_available)
669 setup.fullscreen = FALSE;
671 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
673 SetAudioMode(setup.sound);
677 static int getBeltNrFromBeltElement(int element)
679 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
680 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
681 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
684 static int getBeltNrFromBeltActiveElement(int element)
686 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
687 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
688 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
691 static int getBeltNrFromBeltSwitchElement(int element)
693 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
694 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
695 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
698 static int getBeltDirNrFromBeltSwitchElement(int element)
700 static int belt_base_element[4] =
702 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
703 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
704 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
705 EL_CONVEYOR_BELT_4_SWITCH_LEFT
708 int belt_nr = getBeltNrFromBeltSwitchElement(element);
709 int belt_dir_nr = element - belt_base_element[belt_nr];
711 return (belt_dir_nr % 3);
714 static int getBeltDirFromBeltSwitchElement(int element)
716 static int belt_move_dir[3] =
723 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
725 return belt_move_dir[belt_dir_nr];
728 static void InitPlayerField(int x, int y, int element, boolean init_game)
730 if (element == EL_SP_MURPHY)
734 if (stored_player[0].present)
736 Feld[x][y] = EL_SP_MURPHY_CLONE;
742 stored_player[0].use_murphy = TRUE;
745 Feld[x][y] = EL_PLAYER_1;
751 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
752 int jx = player->jx, jy = player->jy;
754 player->present = TRUE;
756 player->block_last_field = (element == EL_SP_MURPHY ?
757 level.sp_block_last_field :
758 level.block_last_field);
760 /* ---------- initialize player's last field block delay --------------- */
762 /* always start with reliable default value (no adjustment needed) */
763 player->block_delay_adjustment = 0;
765 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
766 if (player->block_last_field && element == EL_SP_MURPHY)
767 player->block_delay_adjustment = 1;
769 /* special case 2: in game engines before 3.1.1, blocking was different */
770 if (game.use_block_last_field_bug)
771 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
773 if (!options.network || player->connected)
775 player->active = TRUE;
777 /* remove potentially duplicate players */
778 if (StorePlayer[jx][jy] == Feld[x][y])
779 StorePlayer[jx][jy] = 0;
781 StorePlayer[x][y] = Feld[x][y];
785 printf("Player %d activated.\n", player->element_nr);
786 printf("[Local player is %d and currently %s.]\n",
787 local_player->element_nr,
788 local_player->active ? "active" : "not active");
792 Feld[x][y] = EL_EMPTY;
794 player->jx = player->last_jx = x;
795 player->jy = player->last_jy = y;
799 static void InitField(int x, int y, boolean init_game)
801 int element = Feld[x][y];
810 InitPlayerField(x, y, element, init_game);
813 case EL_SOKOBAN_FIELD_PLAYER:
814 element = Feld[x][y] = EL_PLAYER_1;
815 InitField(x, y, init_game);
817 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
818 InitField(x, y, init_game);
821 case EL_SOKOBAN_FIELD_EMPTY:
822 local_player->sokobanfields_still_needed++;
826 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
827 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
828 else if (x > 0 && Feld[x-1][y] == EL_ACID)
829 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
830 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
831 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
832 else if (y > 0 && Feld[x][y-1] == EL_ACID)
833 Feld[x][y] = EL_ACID_POOL_BOTTOM;
834 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
835 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
843 case EL_SPACESHIP_RIGHT:
844 case EL_SPACESHIP_UP:
845 case EL_SPACESHIP_LEFT:
846 case EL_SPACESHIP_DOWN:
848 case EL_BD_BUTTERFLY_RIGHT:
849 case EL_BD_BUTTERFLY_UP:
850 case EL_BD_BUTTERFLY_LEFT:
851 case EL_BD_BUTTERFLY_DOWN:
852 case EL_BD_BUTTERFLY:
853 case EL_BD_FIREFLY_RIGHT:
854 case EL_BD_FIREFLY_UP:
855 case EL_BD_FIREFLY_LEFT:
856 case EL_BD_FIREFLY_DOWN:
858 case EL_PACMAN_RIGHT:
882 if (y == lev_fieldy - 1)
884 Feld[x][y] = EL_AMOEBA_GROWING;
885 Store[x][y] = EL_AMOEBA_WET;
889 case EL_DYNAMITE_ACTIVE:
890 case EL_SP_DISK_RED_ACTIVE:
891 case EL_DYNABOMB_PLAYER_1_ACTIVE:
892 case EL_DYNABOMB_PLAYER_2_ACTIVE:
893 case EL_DYNABOMB_PLAYER_3_ACTIVE:
894 case EL_DYNABOMB_PLAYER_4_ACTIVE:
899 local_player->lights_still_needed++;
903 local_player->friends_still_needed++;
908 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
911 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
912 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
913 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
914 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
915 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
916 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
917 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
918 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
919 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
920 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
921 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
922 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
925 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
926 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
927 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
929 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
931 game.belt_dir[belt_nr] = belt_dir;
932 game.belt_dir_nr[belt_nr] = belt_dir_nr;
934 else /* more than one switch -- set it like the first switch */
936 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
941 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
943 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
946 case EL_LIGHT_SWITCH_ACTIVE:
948 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
951 case EL_EMC_MAGIC_BALL:
953 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
956 case EL_EMC_MAGIC_BALL_SWITCH:
958 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
963 if (IS_CUSTOM_ELEMENT(element))
965 if (CAN_MOVE(element))
968 #if USE_NEW_CUSTOM_VALUE
969 if (!element_info[element].use_last_ce_value || init_game)
970 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
974 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
977 else if (IS_GROUP_ELEMENT(element))
979 struct ElementGroupInfo *group = element_info[element].group;
980 int last_anim_random_frame = gfx.anim_random_frame;
983 if (group->choice_mode == ANIM_RANDOM)
984 gfx.anim_random_frame = RND(group->num_elements_resolved);
986 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
987 group->choice_mode, 0,
990 if (group->choice_mode == ANIM_RANDOM)
991 gfx.anim_random_frame = last_anim_random_frame;
995 Feld[x][y] = group->element_resolved[element_pos];
997 InitField(x, y, init_game);
1004 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1009 #if USE_NEW_CUSTOM_VALUE
1012 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1014 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1022 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1024 InitField(x, y, init_game);
1026 /* not needed to call InitMovDir() -- already done by InitField()! */
1027 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1028 CAN_MOVE(Feld[x][y]))
1032 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1034 int old_element = Feld[x][y];
1036 InitField(x, y, init_game);
1038 /* not needed to call InitMovDir() -- already done by InitField()! */
1039 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1040 CAN_MOVE(old_element) &&
1041 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1044 /* this case is in fact a combination of not less than three bugs:
1045 first, it calls InitMovDir() for elements that can move, although this is
1046 already done by InitField(); then, it checks the element that was at this
1047 field _before_ the call to InitField() (which can change it); lastly, it
1048 was not called for "mole with direction" elements, which were treated as
1049 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1053 inline void DrawGameValue_Emeralds(int value)
1055 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1058 inline void DrawGameValue_Dynamite(int value)
1060 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1063 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1067 /* currently only 4 of 8 possible keys are displayed */
1068 for (i = 0; i < STD_NUM_KEYS; i++)
1071 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1072 el2edimg(EL_KEY_1 + i));
1074 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1075 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1076 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1080 inline void DrawGameValue_Score(int value)
1082 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1085 inline void DrawGameValue_Time(int value)
1088 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1090 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1093 inline void DrawGameValue_Level(int value)
1096 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1099 /* misuse area for displaying emeralds to draw bigger level number */
1100 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1101 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1103 /* now copy it to the area for displaying level number */
1104 BlitBitmap(drawto, drawto,
1105 DX_EMERALDS, DY_EMERALDS + 1,
1106 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1107 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1108 DX_LEVEL - 1, DY_LEVEL + 1);
1110 /* restore the area for displaying emeralds */
1111 DrawGameValue_Emeralds(local_player->gems_still_needed);
1113 /* yes, this is all really ugly :-) */
1117 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1120 int key[MAX_NUM_KEYS];
1123 for (i = 0; i < MAX_NUM_KEYS; i++)
1124 key[i] = key_bits & (1 << i);
1126 DrawGameValue_Level(level_nr);
1128 DrawGameValue_Emeralds(emeralds);
1129 DrawGameValue_Dynamite(dynamite);
1130 DrawGameValue_Score(score);
1131 DrawGameValue_Time(time);
1133 DrawGameValue_Keys(key);
1136 void DrawGameDoorValues()
1140 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1142 DrawGameDoorValues_EM();
1147 DrawGameValue_Level(level_nr);
1149 DrawGameValue_Emeralds(local_player->gems_still_needed);
1150 DrawGameValue_Dynamite(local_player->inventory_size);
1151 DrawGameValue_Score(local_player->score);
1152 DrawGameValue_Time(TimeLeft);
1154 for (i = 0; i < MAX_PLAYERS; i++)
1155 DrawGameValue_Keys(stored_player[i].key);
1158 static void resolve_group_element(int group_element, int recursion_depth)
1160 static int group_nr;
1161 static struct ElementGroupInfo *group;
1162 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1165 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1167 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1168 group_element - EL_GROUP_START + 1);
1170 /* replace element which caused too deep recursion by question mark */
1171 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1176 if (recursion_depth == 0) /* initialization */
1178 group = element_info[group_element].group;
1179 group_nr = group_element - EL_GROUP_START;
1181 group->num_elements_resolved = 0;
1182 group->choice_pos = 0;
1185 for (i = 0; i < actual_group->num_elements; i++)
1187 int element = actual_group->element[i];
1189 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1192 if (IS_GROUP_ELEMENT(element))
1193 resolve_group_element(element, recursion_depth + 1);
1196 group->element_resolved[group->num_elements_resolved++] = element;
1197 element_info[element].in_group[group_nr] = TRUE;
1204 =============================================================================
1206 -----------------------------------------------------------------------------
1207 initialize game engine due to level / tape version number
1208 =============================================================================
1211 static void InitGameEngine()
1213 int i, j, k, l, x, y;
1215 /* set game engine from tape file when re-playing, else from level file */
1216 game.engine_version = (tape.playing ? tape.engine_version :
1217 level.game_version);
1219 /* ---------------------------------------------------------------------- */
1220 /* set flags for bugs and changes according to active game engine version */
1221 /* ---------------------------------------------------------------------- */
1224 Summary of bugfix/change:
1225 Fixed handling for custom elements that change when pushed by the player.
1227 Fixed/changed in version:
1231 Before 3.1.0, custom elements that "change when pushing" changed directly
1232 after the player started pushing them (until then handled in "DigField()").
1233 Since 3.1.0, these custom elements are not changed until the "pushing"
1234 move of the element is finished (now handled in "ContinueMoving()").
1236 Affected levels/tapes:
1237 The first condition is generally needed for all levels/tapes before version
1238 3.1.0, which might use the old behaviour before it was changed; known tapes
1239 that are affected are some tapes from the level set "Walpurgis Gardens" by
1241 The second condition is an exception from the above case and is needed for
1242 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1243 above (including some development versions of 3.1.0), but before it was
1244 known that this change would break tapes like the above and was fixed in
1245 3.1.1, so that the changed behaviour was active although the engine version
1246 while recording maybe was before 3.1.0. There is at least one tape that is
1247 affected by this exception, which is the tape for the one-level set "Bug
1248 Machine" by Juergen Bonhagen.
1251 game.use_change_when_pushing_bug =
1252 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1254 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1255 tape.game_version < VERSION_IDENT(3,1,1,0)));
1258 Summary of bugfix/change:
1259 Fixed handling for blocking the field the player leaves when moving.
1261 Fixed/changed in version:
1265 Before 3.1.1, when "block last field when moving" was enabled, the field
1266 the player is leaving when moving was blocked for the time of the move,
1267 and was directly unblocked afterwards. This resulted in the last field
1268 being blocked for exactly one less than the number of frames of one player
1269 move. Additionally, even when blocking was disabled, the last field was
1270 blocked for exactly one frame.
1271 Since 3.1.1, due to changes in player movement handling, the last field
1272 is not blocked at all when blocking is disabled. When blocking is enabled,
1273 the last field is blocked for exactly the number of frames of one player
1274 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1275 last field is blocked for exactly one more than the number of frames of
1278 Affected levels/tapes:
1279 (!!! yet to be determined -- probably many !!!)
1282 game.use_block_last_field_bug =
1283 (game.engine_version < VERSION_IDENT(3,1,1,0));
1286 Summary of bugfix/change:
1287 Changed behaviour of CE changes with multiple changes per single frame.
1289 Fixed/changed in version:
1293 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1294 This resulted in race conditions where CEs seem to behave strange in some
1295 situations (where triggered CE changes were just skipped because there was
1296 already a CE change on that tile in the playfield in that engine frame).
1297 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1298 (The number of changes per frame must be limited in any case, because else
1299 it is easily possible to define CE changes that would result in an infinite
1300 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1301 should be set large enough so that it would only be reached in cases where
1302 the corresponding CE change conditions run into a loop. Therefore, it seems
1303 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1304 maximal number of change pages for custom elements.)
1306 Affected levels/tapes:
1310 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1311 game.max_num_changes_per_frame = 1;
1313 game.max_num_changes_per_frame =
1314 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1317 /* ---------------------------------------------------------------------- */
1319 /* default scan direction: scan playfield from top/left to bottom/right */
1320 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1322 /* dynamically adjust element properties according to game engine version */
1323 InitElementPropertiesEngine(game.engine_version);
1326 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1327 printf(" tape version == %06d [%s] [file: %06d]\n",
1328 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1330 printf(" => game.engine_version == %06d\n", game.engine_version);
1333 /* ---------- recursively resolve group elements ------------------------- */
1335 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1336 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1337 element_info[i].in_group[j] = FALSE;
1339 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1340 resolve_group_element(EL_GROUP_START + i, 0);
1342 /* ---------- initialize player's initial move delay --------------------- */
1344 /* dynamically adjust player properties according to level information */
1345 game.initial_move_delay_value =
1346 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1348 /* dynamically adjust player properties according to game engine version */
1349 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1350 game.initial_move_delay_value : 0);
1352 /* ---------- initialize player's initial push delay --------------------- */
1354 /* dynamically adjust player properties according to game engine version */
1355 game.initial_push_delay_value =
1356 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1358 /* ---------- initialize changing elements ------------------------------- */
1360 /* initialize changing elements information */
1361 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1363 struct ElementInfo *ei = &element_info[i];
1365 /* this pointer might have been changed in the level editor */
1366 ei->change = &ei->change_page[0];
1368 if (!IS_CUSTOM_ELEMENT(i))
1370 ei->change->target_element = EL_EMPTY_SPACE;
1371 ei->change->delay_fixed = 0;
1372 ei->change->delay_random = 0;
1373 ei->change->delay_frames = 1;
1376 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1378 ei->has_change_event[j] = FALSE;
1380 ei->event_page_nr[j] = 0;
1381 ei->event_page[j] = &ei->change_page[0];
1385 /* add changing elements from pre-defined list */
1386 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1388 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1389 struct ElementInfo *ei = &element_info[ch_delay->element];
1391 ei->change->target_element = ch_delay->target_element;
1392 ei->change->delay_fixed = ch_delay->change_delay;
1394 ei->change->pre_change_function = ch_delay->pre_change_function;
1395 ei->change->change_function = ch_delay->change_function;
1396 ei->change->post_change_function = ch_delay->post_change_function;
1398 ei->change->can_change = TRUE;
1399 ei->change->can_change_or_has_action = TRUE;
1401 ei->has_change_event[CE_DELAY] = TRUE;
1403 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1404 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1407 /* ---------- initialize internal run-time variables ------------- */
1409 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1411 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1413 for (j = 0; j < ei->num_change_pages; j++)
1415 ei->change_page[j].can_change_or_has_action =
1416 (ei->change_page[j].can_change |
1417 ei->change_page[j].has_action);
1421 /* add change events from custom element configuration */
1422 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1424 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1426 for (j = 0; j < ei->num_change_pages; j++)
1428 if (!ei->change_page[j].can_change_or_has_action)
1431 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1433 /* only add event page for the first page found with this event */
1434 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1436 ei->has_change_event[k] = TRUE;
1438 ei->event_page_nr[k] = j;
1439 ei->event_page[k] = &ei->change_page[j];
1445 /* ---------- initialize run-time trigger player and element ------------- */
1447 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1449 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1451 for (j = 0; j < ei->num_change_pages; j++)
1453 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1454 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1455 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1456 ei->change_page[j].actual_trigger_ce_value = 0;
1460 /* ---------- initialize trigger events ---------------------------------- */
1462 /* initialize trigger events information */
1463 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1464 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1465 trigger_events[i][j] = FALSE;
1467 /* add trigger events from element change event properties */
1468 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1470 struct ElementInfo *ei = &element_info[i];
1472 for (j = 0; j < ei->num_change_pages; j++)
1474 if (!ei->change_page[j].can_change_or_has_action)
1477 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1479 int trigger_element = ei->change_page[j].trigger_element;
1481 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1483 if (ei->change_page[j].has_event[k])
1485 if (IS_GROUP_ELEMENT(trigger_element))
1487 struct ElementGroupInfo *group =
1488 element_info[trigger_element].group;
1490 for (l = 0; l < group->num_elements_resolved; l++)
1491 trigger_events[group->element_resolved[l]][k] = TRUE;
1494 trigger_events[trigger_element][k] = TRUE;
1501 /* ---------- initialize push delay -------------------------------------- */
1503 /* initialize push delay values to default */
1504 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1506 if (!IS_CUSTOM_ELEMENT(i))
1508 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1509 element_info[i].push_delay_random = game.default_push_delay_random;
1513 /* set push delay value for certain elements from pre-defined list */
1514 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1516 int e = push_delay_list[i].element;
1518 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1519 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1522 /* set push delay value for Supaplex elements for newer engine versions */
1523 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1525 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1527 if (IS_SP_ELEMENT(i))
1529 /* set SP push delay to just enough to push under a falling zonk */
1530 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1532 element_info[i].push_delay_fixed = delay;
1533 element_info[i].push_delay_random = 0;
1538 /* ---------- initialize move stepsize ----------------------------------- */
1540 /* initialize move stepsize values to default */
1541 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1542 if (!IS_CUSTOM_ELEMENT(i))
1543 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1545 /* set move stepsize value for certain elements from pre-defined list */
1546 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1548 int e = move_stepsize_list[i].element;
1550 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1553 /* ---------- initialize collect score ----------------------------------- */
1555 /* initialize collect score values for custom elements from initial value */
1556 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1557 if (IS_CUSTOM_ELEMENT(i))
1558 element_info[i].collect_score = element_info[i].collect_score_initial;
1560 /* ---------- initialize collect count ----------------------------------- */
1562 /* initialize collect count values for non-custom elements */
1563 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1564 if (!IS_CUSTOM_ELEMENT(i))
1565 element_info[i].collect_count_initial = 0;
1567 /* add collect count values for all elements from pre-defined list */
1568 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1569 element_info[collect_count_list[i].element].collect_count_initial =
1570 collect_count_list[i].count;
1572 /* ---------- initialize access direction -------------------------------- */
1574 /* initialize access direction values to default (access from every side) */
1575 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1576 if (!IS_CUSTOM_ELEMENT(i))
1577 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1579 /* set access direction value for certain elements from pre-defined list */
1580 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1581 element_info[access_direction_list[i].element].access_direction =
1582 access_direction_list[i].direction;
1584 /* ---------- initialize explosion content ------------------------------- */
1585 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1587 if (IS_CUSTOM_ELEMENT(i))
1590 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1592 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1594 element_info[i].content.e[x][y] =
1595 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1596 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1597 i == EL_PLAYER_3 ? EL_EMERALD :
1598 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1599 i == EL_MOLE ? EL_EMERALD_RED :
1600 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1601 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1602 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1603 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1604 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1605 i == EL_WALL_EMERALD ? EL_EMERALD :
1606 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1607 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1608 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1609 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1610 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1611 i == EL_WALL_PEARL ? EL_PEARL :
1612 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1618 int get_num_special_action(int element, int action_first, int action_last)
1620 int num_special_action = 0;
1623 for (i = action_first; i <= action_last; i++)
1625 boolean found = FALSE;
1627 for (j = 0; j < NUM_DIRECTIONS; j++)
1628 if (el_act_dir2img(element, i, j) !=
1629 el_act_dir2img(element, ACTION_DEFAULT, j))
1633 num_special_action++;
1638 return num_special_action;
1642 =============================================================================
1644 -----------------------------------------------------------------------------
1645 initialize and start new game
1646 =============================================================================
1651 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1652 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1653 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1658 /* don't play tapes over network */
1659 network_playing = (options.network && !tape.playing);
1661 for (i = 0; i < MAX_PLAYERS; i++)
1663 struct PlayerInfo *player = &stored_player[i];
1665 player->index_nr = i;
1666 player->index_bit = (1 << i);
1667 player->element_nr = EL_PLAYER_1 + i;
1669 player->present = FALSE;
1670 player->active = FALSE;
1673 player->effective_action = 0;
1674 player->programmed_action = 0;
1677 player->gems_still_needed = level.gems_needed;
1678 player->sokobanfields_still_needed = 0;
1679 player->lights_still_needed = 0;
1680 player->friends_still_needed = 0;
1682 for (j = 0; j < MAX_NUM_KEYS; j++)
1683 player->key[j] = FALSE;
1685 player->dynabomb_count = 0;
1686 player->dynabomb_size = 1;
1687 player->dynabombs_left = 0;
1688 player->dynabomb_xl = FALSE;
1690 player->MovDir = MV_NONE;
1693 player->GfxDir = MV_NONE;
1694 player->GfxAction = ACTION_DEFAULT;
1696 player->StepFrame = 0;
1698 player->use_murphy = FALSE;
1699 player->artwork_element =
1700 (level.use_artwork_element[i] ? level.artwork_element[i] :
1701 player->element_nr);
1703 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1704 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1706 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1708 player->actual_frame_counter = 0;
1710 player->step_counter = 0;
1712 player->last_move_dir = MV_NONE;
1714 player->is_waiting = FALSE;
1715 player->is_moving = FALSE;
1716 player->is_auto_moving = FALSE;
1717 player->is_digging = FALSE;
1718 player->is_snapping = FALSE;
1719 player->is_collecting = FALSE;
1720 player->is_pushing = FALSE;
1721 player->is_switching = FALSE;
1722 player->is_dropping = FALSE;
1724 player->is_bored = FALSE;
1725 player->is_sleeping = FALSE;
1727 player->cannot_move = FALSE;
1729 player->frame_counter_bored = -1;
1730 player->frame_counter_sleeping = -1;
1732 player->anim_delay_counter = 0;
1733 player->post_delay_counter = 0;
1735 player->action_waiting = ACTION_DEFAULT;
1736 player->last_action_waiting = ACTION_DEFAULT;
1737 player->special_action_bored = ACTION_DEFAULT;
1738 player->special_action_sleeping = ACTION_DEFAULT;
1740 /* set number of special actions for bored and sleeping animation */
1741 player->num_special_action_bored =
1742 get_num_special_action(player->artwork_element,
1743 ACTION_BORING_1, ACTION_BORING_LAST);
1744 player->num_special_action_sleeping =
1745 get_num_special_action(player->artwork_element,
1746 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1748 player->switch_x = -1;
1749 player->switch_y = -1;
1751 player->drop_x = -1;
1752 player->drop_y = -1;
1754 player->show_envelope = 0;
1756 player->move_delay = game.initial_move_delay;
1757 player->move_delay_value = game.initial_move_delay_value;
1759 player->move_delay_value_next = -1;
1761 player->move_delay_reset_counter = 0;
1763 player->push_delay = -1; /* initialized when pushing starts */
1764 player->push_delay_value = game.initial_push_delay_value;
1766 player->drop_delay = 0;
1768 player->last_jx = player->last_jy = 0;
1769 player->jx = player->jy = 0;
1771 player->shield_normal_time_left = 0;
1772 player->shield_deadly_time_left = 0;
1774 player->inventory_infinite_element = EL_UNDEFINED;
1775 player->inventory_size = 0;
1777 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1778 SnapField(player, 0, 0);
1780 player->LevelSolved = FALSE;
1781 player->GameOver = FALSE;
1784 network_player_action_received = FALSE;
1786 #if defined(NETWORK_AVALIABLE)
1787 /* initial null action */
1788 if (network_playing)
1789 SendToServer_MovePlayer(MV_NONE);
1798 TimeLeft = level.time;
1801 ScreenMovDir = MV_NONE;
1805 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1807 AllPlayersGone = FALSE;
1809 game.yamyam_content_nr = 0;
1810 game.magic_wall_active = FALSE;
1811 game.magic_wall_time_left = 0;
1812 game.light_time_left = 0;
1813 game.timegate_time_left = 0;
1814 game.switchgate_pos = 0;
1815 game.wind_direction = level.wind_direction_initial;
1816 game.gravity = level.initial_gravity;
1817 game.explosions_delayed = TRUE;
1819 game.lenses_time_left = 0;
1820 game.magnify_time_left = 0;
1822 game.ball_state = level.ball_state_initial;
1823 game.ball_content_nr = 0;
1825 game.envelope_active = FALSE;
1827 for (i = 0; i < NUM_BELTS; i++)
1829 game.belt_dir[i] = MV_NONE;
1830 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1833 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1834 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1837 SCAN_PLAYFIELD(x, y)
1839 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1842 Feld[x][y] = level.field[x][y];
1843 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1844 ChangeDelay[x][y] = 0;
1845 ChangePage[x][y] = -1;
1846 #if USE_NEW_CUSTOM_VALUE
1847 CustomValue[x][y] = 0; /* initialized in InitField() */
1849 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1851 WasJustMoving[x][y] = 0;
1852 WasJustFalling[x][y] = 0;
1853 CheckCollision[x][y] = 0;
1855 Pushed[x][y] = FALSE;
1857 ChangeCount[x][y] = 0;
1858 ChangeEvent[x][y] = -1;
1860 ExplodePhase[x][y] = 0;
1861 ExplodeDelay[x][y] = 0;
1862 ExplodeField[x][y] = EX_TYPE_NONE;
1864 RunnerVisit[x][y] = 0;
1865 PlayerVisit[x][y] = 0;
1868 GfxRandom[x][y] = INIT_GFX_RANDOM();
1869 GfxElement[x][y] = EL_UNDEFINED;
1870 GfxAction[x][y] = ACTION_DEFAULT;
1871 GfxDir[x][y] = MV_NONE;
1875 SCAN_PLAYFIELD(x, y)
1877 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1880 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1882 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1884 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1887 InitField(x, y, TRUE);
1892 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1893 emulate_sb ? EMU_SOKOBAN :
1894 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1896 #if USE_NEW_ALL_SLIPPERY
1897 /* initialize type of slippery elements */
1898 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1900 if (!IS_CUSTOM_ELEMENT(i))
1902 /* default: elements slip down either to the left or right randomly */
1903 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1905 /* SP style elements prefer to slip down on the left side */
1906 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1907 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1909 /* BD style elements prefer to slip down on the left side */
1910 if (game.emulation == EMU_BOULDERDASH)
1911 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1916 /* initialize explosion and ignition delay */
1917 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1919 if (!IS_CUSTOM_ELEMENT(i))
1922 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1923 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1924 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1925 int last_phase = (num_phase + 1) * delay;
1926 int half_phase = (num_phase / 2) * delay;
1928 element_info[i].explosion_delay = last_phase - 1;
1929 element_info[i].ignition_delay = half_phase;
1931 if (i == EL_BLACK_ORB)
1932 element_info[i].ignition_delay = 1;
1936 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1937 element_info[i].explosion_delay = 1;
1939 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1940 element_info[i].ignition_delay = 1;
1944 /* correct non-moving belts to start moving left */
1945 for (i = 0; i < NUM_BELTS; i++)
1946 if (game.belt_dir[i] == MV_NONE)
1947 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1949 /* check if any connected player was not found in playfield */
1950 for (i = 0; i < MAX_PLAYERS; i++)
1952 struct PlayerInfo *player = &stored_player[i];
1954 if (player->connected && !player->present)
1956 for (j = 0; j < MAX_PLAYERS; j++)
1958 struct PlayerInfo *some_player = &stored_player[j];
1959 int jx = some_player->jx, jy = some_player->jy;
1961 /* assign first free player found that is present in the playfield */
1962 if (some_player->present && !some_player->connected)
1964 player->present = TRUE;
1965 player->active = TRUE;
1967 some_player->present = FALSE;
1968 some_player->active = FALSE;
1971 player->element_nr = some_player->element_nr;
1974 player->artwork_element = some_player->artwork_element;
1976 player->block_last_field = some_player->block_last_field;
1977 player->block_delay_adjustment = some_player->block_delay_adjustment;
1979 StorePlayer[jx][jy] = player->element_nr;
1980 player->jx = player->last_jx = jx;
1981 player->jy = player->last_jy = jy;
1991 /* when playing a tape, eliminate all players which do not participate */
1993 for (i = 0; i < MAX_PLAYERS; i++)
1995 if (stored_player[i].active && !tape.player_participates[i])
1997 struct PlayerInfo *player = &stored_player[i];
1998 int jx = player->jx, jy = player->jy;
2000 player->active = FALSE;
2001 StorePlayer[jx][jy] = 0;
2002 Feld[jx][jy] = EL_EMPTY;
2006 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2008 /* when in single player mode, eliminate all but the first active player */
2010 for (i = 0; i < MAX_PLAYERS; i++)
2012 if (stored_player[i].active)
2014 for (j = i + 1; j < MAX_PLAYERS; j++)
2016 if (stored_player[j].active)
2018 struct PlayerInfo *player = &stored_player[j];
2019 int jx = player->jx, jy = player->jy;
2021 player->active = FALSE;
2022 player->present = FALSE;
2024 StorePlayer[jx][jy] = 0;
2025 Feld[jx][jy] = EL_EMPTY;
2032 /* when recording the game, store which players take part in the game */
2035 for (i = 0; i < MAX_PLAYERS; i++)
2036 if (stored_player[i].active)
2037 tape.player_participates[i] = TRUE;
2042 for (i = 0; i < MAX_PLAYERS; i++)
2044 struct PlayerInfo *player = &stored_player[i];
2046 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2051 if (local_player == player)
2052 printf("Player %d is local player.\n", i+1);
2056 if (BorderElement == EL_EMPTY)
2059 SBX_Right = lev_fieldx - SCR_FIELDX;
2061 SBY_Lower = lev_fieldy - SCR_FIELDY;
2066 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2068 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2071 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2072 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2074 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2075 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2077 /* if local player not found, look for custom element that might create
2078 the player (make some assumptions about the right custom element) */
2079 if (!local_player->present)
2081 int start_x = 0, start_y = 0;
2082 int found_rating = 0;
2083 int found_element = EL_UNDEFINED;
2084 int player_nr = local_player->index_nr;
2087 SCAN_PLAYFIELD(x, y)
2089 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2092 int element = Feld[x][y];
2097 if (level.use_start_element[player_nr] &&
2098 level.start_element[player_nr] == element &&
2105 found_element = element;
2108 if (!IS_CUSTOM_ELEMENT(element))
2111 if (CAN_CHANGE(element))
2113 for (i = 0; i < element_info[element].num_change_pages; i++)
2115 /* check for player created from custom element as single target */
2116 content = element_info[element].change_page[i].target_element;
2117 is_player = ELEM_IS_PLAYER(content);
2119 if (is_player && (found_rating < 3 || element < found_element))
2125 found_element = element;
2130 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2132 /* check for player created from custom element as explosion content */
2133 content = element_info[element].content.e[xx][yy];
2134 is_player = ELEM_IS_PLAYER(content);
2136 if (is_player && (found_rating < 2 || element < found_element))
2138 start_x = x + xx - 1;
2139 start_y = y + yy - 1;
2142 found_element = element;
2145 if (!CAN_CHANGE(element))
2148 for (i = 0; i < element_info[element].num_change_pages; i++)
2150 /* check for player created from custom element as extended target */
2152 element_info[element].change_page[i].target_content.e[xx][yy];
2154 is_player = ELEM_IS_PLAYER(content);
2156 if (is_player && (found_rating < 1 || element < found_element))
2158 start_x = x + xx - 1;
2159 start_y = y + yy - 1;
2162 found_element = element;
2168 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2169 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2172 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2173 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2178 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2179 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2180 local_player->jx - MIDPOSX);
2182 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2183 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2184 local_player->jy - MIDPOSY);
2187 if (!game.restart_level)
2188 CloseDoor(DOOR_CLOSE_1);
2190 /* !!! FIX THIS (START) !!! */
2191 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2193 InitGameEngine_EM();
2200 /* after drawing the level, correct some elements */
2201 if (game.timegate_time_left == 0)
2202 CloseAllOpenTimegates();
2204 if (setup.soft_scrolling)
2205 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2207 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2210 /* !!! FIX THIS (END) !!! */
2212 if (!game.restart_level)
2214 /* copy default game door content to main double buffer */
2215 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2216 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2219 DrawGameDoorValues();
2221 if (!game.restart_level)
2225 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2226 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2227 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2231 /* copy actual game door content to door double buffer for OpenDoor() */
2232 BlitBitmap(drawto, bitmap_db_door,
2233 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2235 OpenDoor(DOOR_OPEN_ALL);
2237 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2239 if (setup.sound_music)
2242 KeyboardAutoRepeatOffUnlessAutoplay();
2246 for (i = 0; i < MAX_PLAYERS; i++)
2247 printf("Player %d %sactive.\n",
2248 i + 1, (stored_player[i].active ? "" : "not "));
2252 game.restart_level = FALSE;
2255 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2257 /* this is used for non-R'n'D game engines to update certain engine values */
2259 /* needed to determine if sounds are played within the visible screen area */
2260 scroll_x = actual_scroll_x;
2261 scroll_y = actual_scroll_y;
2264 void InitMovDir(int x, int y)
2266 int i, element = Feld[x][y];
2267 static int xy[4][2] =
2274 static int direction[3][4] =
2276 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2277 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2278 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2287 Feld[x][y] = EL_BUG;
2288 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2291 case EL_SPACESHIP_RIGHT:
2292 case EL_SPACESHIP_UP:
2293 case EL_SPACESHIP_LEFT:
2294 case EL_SPACESHIP_DOWN:
2295 Feld[x][y] = EL_SPACESHIP;
2296 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2299 case EL_BD_BUTTERFLY_RIGHT:
2300 case EL_BD_BUTTERFLY_UP:
2301 case EL_BD_BUTTERFLY_LEFT:
2302 case EL_BD_BUTTERFLY_DOWN:
2303 Feld[x][y] = EL_BD_BUTTERFLY;
2304 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2307 case EL_BD_FIREFLY_RIGHT:
2308 case EL_BD_FIREFLY_UP:
2309 case EL_BD_FIREFLY_LEFT:
2310 case EL_BD_FIREFLY_DOWN:
2311 Feld[x][y] = EL_BD_FIREFLY;
2312 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2315 case EL_PACMAN_RIGHT:
2317 case EL_PACMAN_LEFT:
2318 case EL_PACMAN_DOWN:
2319 Feld[x][y] = EL_PACMAN;
2320 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2323 case EL_SP_SNIKSNAK:
2324 MovDir[x][y] = MV_UP;
2327 case EL_SP_ELECTRON:
2328 MovDir[x][y] = MV_LEFT;
2335 Feld[x][y] = EL_MOLE;
2336 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2340 if (IS_CUSTOM_ELEMENT(element))
2342 struct ElementInfo *ei = &element_info[element];
2343 int move_direction_initial = ei->move_direction_initial;
2344 int move_pattern = ei->move_pattern;
2346 if (move_direction_initial == MV_START_PREVIOUS)
2348 if (MovDir[x][y] != MV_NONE)
2351 move_direction_initial = MV_START_AUTOMATIC;
2354 if (move_direction_initial == MV_START_RANDOM)
2355 MovDir[x][y] = 1 << RND(4);
2356 else if (move_direction_initial & MV_ANY_DIRECTION)
2357 MovDir[x][y] = move_direction_initial;
2358 else if (move_pattern == MV_ALL_DIRECTIONS ||
2359 move_pattern == MV_TURNING_LEFT ||
2360 move_pattern == MV_TURNING_RIGHT ||
2361 move_pattern == MV_TURNING_LEFT_RIGHT ||
2362 move_pattern == MV_TURNING_RIGHT_LEFT ||
2363 move_pattern == MV_TURNING_RANDOM)
2364 MovDir[x][y] = 1 << RND(4);
2365 else if (move_pattern == MV_HORIZONTAL)
2366 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2367 else if (move_pattern == MV_VERTICAL)
2368 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2369 else if (move_pattern & MV_ANY_DIRECTION)
2370 MovDir[x][y] = element_info[element].move_pattern;
2371 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2372 move_pattern == MV_ALONG_RIGHT_SIDE)
2374 /* use random direction as default start direction */
2375 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2376 MovDir[x][y] = 1 << RND(4);
2378 for (i = 0; i < NUM_DIRECTIONS; i++)
2380 int x1 = x + xy[i][0];
2381 int y1 = y + xy[i][1];
2383 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2385 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2386 MovDir[x][y] = direction[0][i];
2388 MovDir[x][y] = direction[1][i];
2397 MovDir[x][y] = 1 << RND(4);
2399 if (element != EL_BUG &&
2400 element != EL_SPACESHIP &&
2401 element != EL_BD_BUTTERFLY &&
2402 element != EL_BD_FIREFLY)
2405 for (i = 0; i < NUM_DIRECTIONS; i++)
2407 int x1 = x + xy[i][0];
2408 int y1 = y + xy[i][1];
2410 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2412 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2414 MovDir[x][y] = direction[0][i];
2417 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2418 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2420 MovDir[x][y] = direction[1][i];
2429 GfxDir[x][y] = MovDir[x][y];
2432 void InitAmoebaNr(int x, int y)
2435 int group_nr = AmoebeNachbarNr(x, y);
2439 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2441 if (AmoebaCnt[i] == 0)
2449 AmoebaNr[x][y] = group_nr;
2450 AmoebaCnt[group_nr]++;
2451 AmoebaCnt2[group_nr]++;
2457 boolean raise_level = FALSE;
2459 if (local_player->MovPos)
2462 if (tape.auto_play) /* tape might already be stopped here */
2463 tape.auto_play_level_solved = TRUE;
2465 local_player->LevelSolved = FALSE;
2467 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2471 if (!tape.playing && setup.sound_loops)
2472 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2473 SND_CTRL_PLAY_LOOP);
2475 while (TimeLeft > 0)
2477 if (!tape.playing && !setup.sound_loops)
2478 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2480 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2483 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2488 RaiseScore(level.score[SC_TIME_BONUS]);
2491 DrawGameValue_Time(TimeLeft);
2499 if (!tape.playing && setup.sound_loops)
2500 StopSound(SND_GAME_LEVELTIME_BONUS);
2502 else if (level.time == 0) /* level without time limit */
2504 if (!tape.playing && setup.sound_loops)
2505 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2506 SND_CTRL_PLAY_LOOP);
2508 while (TimePlayed < 999)
2510 if (!tape.playing && !setup.sound_loops)
2511 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2513 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2516 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2521 RaiseScore(level.score[SC_TIME_BONUS]);
2524 DrawGameValue_Time(TimePlayed);
2532 if (!tape.playing && setup.sound_loops)
2533 StopSound(SND_GAME_LEVELTIME_BONUS);
2536 /* close exit door after last player */
2537 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2538 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2539 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2541 int element = Feld[ExitX][ExitY];
2543 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2544 EL_SP_EXIT_CLOSING);
2546 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2549 /* player disappears */
2550 if (ExitX >= 0 && ExitY >= 0)
2551 DrawLevelField(ExitX, ExitY);
2558 CloseDoor(DOOR_CLOSE_1);
2563 SaveTape(tape.level_nr); /* Ask to save tape */
2566 if (level_nr == leveldir_current->handicap_level)
2568 leveldir_current->handicap_level++;
2569 SaveLevelSetup_SeriesInfo();
2572 if (level_editor_test_game)
2573 local_player->score = -1; /* no highscore when playing from editor */
2574 else if (level_nr < leveldir_current->last_level)
2575 raise_level = TRUE; /* advance to next level */
2577 if ((hi_pos = NewHiScore()) >= 0)
2579 game_status = GAME_MODE_SCORES;
2580 DrawHallOfFame(hi_pos);
2589 game_status = GAME_MODE_MAIN;
2606 LoadScore(level_nr);
2608 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2609 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2612 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2614 if (local_player->score > highscore[k].Score)
2616 /* player has made it to the hall of fame */
2618 if (k < MAX_SCORE_ENTRIES - 1)
2620 int m = MAX_SCORE_ENTRIES - 1;
2623 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2624 if (!strcmp(setup.player_name, highscore[l].Name))
2626 if (m == k) /* player's new highscore overwrites his old one */
2630 for (l = m; l > k; l--)
2632 strcpy(highscore[l].Name, highscore[l - 1].Name);
2633 highscore[l].Score = highscore[l - 1].Score;
2640 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2641 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2642 highscore[k].Score = local_player->score;
2648 else if (!strncmp(setup.player_name, highscore[k].Name,
2649 MAX_PLAYER_NAME_LEN))
2650 break; /* player already there with a higher score */
2656 SaveScore(level_nr);
2661 inline static int getElementMoveStepsize(int x, int y)
2663 int element = Feld[x][y];
2664 int direction = MovDir[x][y];
2665 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2666 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2667 int horiz_move = (dx != 0);
2668 int sign = (horiz_move ? dx : dy);
2669 int step = sign * element_info[element].move_stepsize;
2671 /* special values for move stepsize for spring and things on conveyor belt */
2675 if (element == EL_SPRING)
2676 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2677 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2678 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2679 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2681 if (CAN_FALL(element) &&
2682 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2683 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2684 else if (element == EL_SPRING)
2685 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2692 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2694 if (player->GfxAction != action || player->GfxDir != dir)
2697 printf("Player frame reset! (%d => %d, %d => %d)\n",
2698 player->GfxAction, action, player->GfxDir, dir);
2701 player->GfxAction = action;
2702 player->GfxDir = dir;
2704 player->StepFrame = 0;
2708 static void ResetRandomAnimationValue(int x, int y)
2710 GfxRandom[x][y] = INIT_GFX_RANDOM();
2713 static void ResetGfxAnimation(int x, int y)
2716 GfxAction[x][y] = ACTION_DEFAULT;
2717 GfxDir[x][y] = MovDir[x][y];
2720 void InitMovingField(int x, int y, int direction)
2722 int element = Feld[x][y];
2723 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2724 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2728 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2729 ResetGfxAnimation(x, y);
2731 MovDir[x][y] = direction;
2732 GfxDir[x][y] = direction;
2733 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2734 ACTION_FALLING : ACTION_MOVING);
2736 /* this is needed for CEs with property "can move" / "not moving" */
2738 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2740 if (Feld[newx][newy] == EL_EMPTY)
2741 Feld[newx][newy] = EL_BLOCKED;
2743 MovDir[newx][newy] = MovDir[x][y];
2745 #if USE_NEW_CUSTOM_VALUE
2746 CustomValue[newx][newy] = CustomValue[x][y];
2749 GfxFrame[newx][newy] = GfxFrame[x][y];
2750 GfxRandom[newx][newy] = GfxRandom[x][y];
2751 GfxAction[newx][newy] = GfxAction[x][y];
2752 GfxDir[newx][newy] = GfxDir[x][y];
2756 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2758 int direction = MovDir[x][y];
2759 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2760 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2766 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2768 int oldx = x, oldy = y;
2769 int direction = MovDir[x][y];
2771 if (direction == MV_LEFT)
2773 else if (direction == MV_RIGHT)
2775 else if (direction == MV_UP)
2777 else if (direction == MV_DOWN)
2780 *comes_from_x = oldx;
2781 *comes_from_y = oldy;
2784 int MovingOrBlocked2Element(int x, int y)
2786 int element = Feld[x][y];
2788 if (element == EL_BLOCKED)
2792 Blocked2Moving(x, y, &oldx, &oldy);
2793 return Feld[oldx][oldy];
2799 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2801 /* like MovingOrBlocked2Element(), but if element is moving
2802 and (x,y) is the field the moving element is just leaving,
2803 return EL_BLOCKED instead of the element value */
2804 int element = Feld[x][y];
2806 if (IS_MOVING(x, y))
2808 if (element == EL_BLOCKED)
2812 Blocked2Moving(x, y, &oldx, &oldy);
2813 return Feld[oldx][oldy];
2822 static void RemoveField(int x, int y)
2824 Feld[x][y] = EL_EMPTY;
2830 #if USE_NEW_CUSTOM_VALUE
2831 CustomValue[x][y] = 0;
2835 ChangeDelay[x][y] = 0;
2836 ChangePage[x][y] = -1;
2837 Pushed[x][y] = FALSE;
2840 ExplodeField[x][y] = EX_TYPE_NONE;
2843 GfxElement[x][y] = EL_UNDEFINED;
2844 GfxAction[x][y] = ACTION_DEFAULT;
2845 GfxDir[x][y] = MV_NONE;
2848 void RemoveMovingField(int x, int y)
2850 int oldx = x, oldy = y, newx = x, newy = y;
2851 int element = Feld[x][y];
2852 int next_element = EL_UNDEFINED;
2854 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2857 if (IS_MOVING(x, y))
2859 Moving2Blocked(x, y, &newx, &newy);
2861 if (Feld[newx][newy] != EL_BLOCKED)
2863 /* element is moving, but target field is not free (blocked), but
2864 already occupied by something different (example: acid pool);
2865 in this case, only remove the moving field, but not the target */
2867 RemoveField(oldx, oldy);
2869 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2871 DrawLevelField(oldx, oldy);
2876 else if (element == EL_BLOCKED)
2878 Blocked2Moving(x, y, &oldx, &oldy);
2879 if (!IS_MOVING(oldx, oldy))
2883 if (element == EL_BLOCKED &&
2884 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2885 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2886 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2887 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2888 next_element = get_next_element(Feld[oldx][oldy]);
2890 RemoveField(oldx, oldy);
2891 RemoveField(newx, newy);
2893 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2895 if (next_element != EL_UNDEFINED)
2896 Feld[oldx][oldy] = next_element;
2898 DrawLevelField(oldx, oldy);
2899 DrawLevelField(newx, newy);
2902 void DrawDynamite(int x, int y)
2904 int sx = SCREENX(x), sy = SCREENY(y);
2905 int graphic = el2img(Feld[x][y]);
2908 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2911 if (IS_WALKABLE_INSIDE(Back[x][y]))
2915 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2916 else if (Store[x][y])
2917 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2919 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2921 if (Back[x][y] || Store[x][y])
2922 DrawGraphicThruMask(sx, sy, graphic, frame);
2924 DrawGraphic(sx, sy, graphic, frame);
2927 void CheckDynamite(int x, int y)
2929 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2933 if (MovDelay[x][y] != 0)
2936 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2942 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2947 void DrawRelocatePlayer(struct PlayerInfo *player)
2949 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2950 boolean no_delay = (tape.warp_forward);
2951 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2952 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2953 int jx = player->jx;
2954 int jy = player->jy;
2956 if (level.instant_relocation)
2958 int offset = (setup.scroll_delay ? 3 : 0);
2960 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2962 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2963 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2964 local_player->jx - MIDPOSX);
2966 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2967 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2968 local_player->jy - MIDPOSY);
2972 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2973 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2974 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2976 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2977 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2978 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2980 /* don't scroll over playfield boundaries */
2981 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2982 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2984 /* don't scroll over playfield boundaries */
2985 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2986 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2989 RedrawPlayfield(TRUE, 0,0,0,0);
2993 int scroll_xx = -999, scroll_yy = -999;
2995 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2997 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3000 int fx = FX, fy = FY;
3002 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3003 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3004 local_player->jx - MIDPOSX);
3006 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3007 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3008 local_player->jy - MIDPOSY);
3010 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3011 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3013 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3019 fx += dx * TILEX / 2;
3020 fy += dy * TILEY / 2;
3022 ScrollLevel(dx, dy);
3025 /* scroll in two steps of half tile size to make things smoother */
3026 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3028 Delay(wait_delay_value);
3030 /* scroll second step to align at full tile size */
3032 Delay(wait_delay_value);
3037 Delay(wait_delay_value);
3041 void RelocatePlayer(int jx, int jy, int el_player_raw)
3043 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3044 int player_nr = GET_PLAYER_NR(el_player);
3045 struct PlayerInfo *player = &stored_player[player_nr];
3046 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3047 boolean no_delay = (tape.warp_forward);
3048 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3049 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3050 int old_jx = player->jx;
3051 int old_jy = player->jy;
3052 int old_element = Feld[old_jx][old_jy];
3053 int element = Feld[jx][jy];
3054 boolean player_relocated = (old_jx != jx || old_jy != jy);
3056 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3057 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3058 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3059 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3060 int leave_side_horiz = move_dir_horiz;
3061 int leave_side_vert = move_dir_vert;
3062 int enter_side = enter_side_horiz | enter_side_vert;
3063 int leave_side = leave_side_horiz | leave_side_vert;
3065 if (player->GameOver) /* do not reanimate dead player */
3068 if (!player_relocated) /* no need to relocate the player */
3071 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3073 RemoveField(jx, jy); /* temporarily remove newly placed player */
3074 DrawLevelField(jx, jy);
3077 if (player->present)
3079 while (player->MovPos)
3081 ScrollPlayer(player, SCROLL_GO_ON);
3082 ScrollScreen(NULL, SCROLL_GO_ON);
3084 AdvanceFrameAndPlayerCounters(player->index_nr);
3089 Delay(wait_delay_value);
3092 DrawPlayer(player); /* needed here only to cleanup last field */
3093 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3095 player->is_moving = FALSE;
3098 if (IS_CUSTOM_ELEMENT(old_element))
3099 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3101 player->index_bit, leave_side);
3103 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3105 player->index_bit, leave_side);
3107 Feld[jx][jy] = el_player;
3108 InitPlayerField(jx, jy, el_player, TRUE);
3110 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3112 Feld[jx][jy] = element;
3113 InitField(jx, jy, FALSE);
3116 if (player == local_player) /* only visually relocate local player */
3117 DrawRelocatePlayer(player);
3119 TestIfPlayerTouchesBadThing(jx, jy);
3120 TestIfPlayerTouchesCustomElement(jx, jy);
3122 if (IS_CUSTOM_ELEMENT(element))
3123 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3124 player->index_bit, enter_side);
3126 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3127 player->index_bit, enter_side);
3130 void Explode(int ex, int ey, int phase, int mode)
3136 /* !!! eliminate this variable !!! */
3137 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3139 if (game.explosions_delayed)
3141 ExplodeField[ex][ey] = mode;
3145 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3147 int center_element = Feld[ex][ey];
3148 int artwork_element, explosion_element; /* set these values later */
3151 /* --- This is only really needed (and now handled) in "Impact()". --- */
3152 /* do not explode moving elements that left the explode field in time */
3153 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3154 center_element == EL_EMPTY &&
3155 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3160 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3161 if (mode == EX_TYPE_NORMAL ||
3162 mode == EX_TYPE_CENTER ||
3163 mode == EX_TYPE_CROSS)
3164 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3167 /* remove things displayed in background while burning dynamite */
3168 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3171 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3173 /* put moving element to center field (and let it explode there) */
3174 center_element = MovingOrBlocked2Element(ex, ey);
3175 RemoveMovingField(ex, ey);
3176 Feld[ex][ey] = center_element;
3179 /* now "center_element" is finally determined -- set related values now */
3180 artwork_element = center_element; /* for custom player artwork */
3181 explosion_element = center_element; /* for custom player artwork */
3183 if (IS_PLAYER(ex, ey))
3185 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3187 artwork_element = stored_player[player_nr].artwork_element;
3189 if (level.use_explosion_element[player_nr])
3191 explosion_element = level.explosion_element[player_nr];
3192 artwork_element = explosion_element;
3197 if (mode == EX_TYPE_NORMAL ||
3198 mode == EX_TYPE_CENTER ||
3199 mode == EX_TYPE_CROSS)
3200 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3204 last_phase = element_info[explosion_element].explosion_delay + 1;
3206 last_phase = element_info[center_element].explosion_delay + 1;
3209 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3211 int xx = x - ex + 1;
3212 int yy = y - ey + 1;
3215 if (!IN_LEV_FIELD(x, y) ||
3216 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3217 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3220 element = Feld[x][y];
3222 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3224 element = MovingOrBlocked2Element(x, y);
3226 if (!IS_EXPLOSION_PROOF(element))
3227 RemoveMovingField(x, y);
3230 /* indestructible elements can only explode in center (but not flames) */
3231 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3232 mode == EX_TYPE_BORDER)) ||
3233 element == EL_FLAMES)
3236 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3237 behaviour, for example when touching a yamyam that explodes to rocks
3238 with active deadly shield, a rock is created under the player !!! */
3239 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3241 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3242 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3243 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3245 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3248 if (IS_ACTIVE_BOMB(element))
3250 /* re-activate things under the bomb like gate or penguin */
3251 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3258 /* save walkable background elements while explosion on same tile */
3259 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3260 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3261 Back[x][y] = element;
3263 /* ignite explodable elements reached by other explosion */
3264 if (element == EL_EXPLOSION)
3265 element = Store2[x][y];
3267 if (AmoebaNr[x][y] &&
3268 (element == EL_AMOEBA_FULL ||
3269 element == EL_BD_AMOEBA ||
3270 element == EL_AMOEBA_GROWING))
3272 AmoebaCnt[AmoebaNr[x][y]]--;
3273 AmoebaCnt2[AmoebaNr[x][y]]--;
3278 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3281 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3283 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3285 switch(StorePlayer[ex][ey])
3288 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3291 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3294 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3298 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3303 if (PLAYERINFO(ex, ey)->use_murphy)
3304 Store[x][y] = EL_EMPTY;
3307 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3308 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3309 else if (ELEM_IS_PLAYER(center_element))
3310 Store[x][y] = EL_EMPTY;
3311 else if (center_element == EL_YAMYAM)
3312 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3313 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3314 Store[x][y] = element_info[center_element].content.e[xx][yy];
3316 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3317 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3318 otherwise) -- FIX THIS !!! */
3319 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3320 Store[x][y] = element_info[element].content.e[1][1];
3322 else if (!CAN_EXPLODE(element))
3323 Store[x][y] = element_info[element].content.e[1][1];
3326 Store[x][y] = EL_EMPTY;
3328 else if (center_element == EL_MOLE)
3329 Store[x][y] = EL_EMERALD_RED;
3330 else if (center_element == EL_PENGUIN)
3331 Store[x][y] = EL_EMERALD_PURPLE;
3332 else if (center_element == EL_BUG)
3333 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3334 else if (center_element == EL_BD_BUTTERFLY)
3335 Store[x][y] = EL_BD_DIAMOND;
3336 else if (center_element == EL_SP_ELECTRON)
3337 Store[x][y] = EL_SP_INFOTRON;
3338 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3339 Store[x][y] = level.amoeba_content;
3340 else if (center_element == EL_YAMYAM)
3341 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3342 else if (IS_CUSTOM_ELEMENT(center_element) &&
3343 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3344 Store[x][y] = element_info[center_element].content.e[xx][yy];
3345 else if (element == EL_WALL_EMERALD)
3346 Store[x][y] = EL_EMERALD;
3347 else if (element == EL_WALL_DIAMOND)
3348 Store[x][y] = EL_DIAMOND;
3349 else if (element == EL_WALL_BD_DIAMOND)
3350 Store[x][y] = EL_BD_DIAMOND;
3351 else if (element == EL_WALL_EMERALD_YELLOW)
3352 Store[x][y] = EL_EMERALD_YELLOW;
3353 else if (element == EL_WALL_EMERALD_RED)
3354 Store[x][y] = EL_EMERALD_RED;
3355 else if (element == EL_WALL_EMERALD_PURPLE)
3356 Store[x][y] = EL_EMERALD_PURPLE;
3357 else if (element == EL_WALL_PEARL)
3358 Store[x][y] = EL_PEARL;
3359 else if (element == EL_WALL_CRYSTAL)
3360 Store[x][y] = EL_CRYSTAL;
3361 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3362 Store[x][y] = element_info[element].content.e[1][1];
3364 Store[x][y] = EL_EMPTY;
3367 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3368 center_element == EL_AMOEBA_TO_DIAMOND)
3369 Store2[x][y] = element;
3371 Feld[x][y] = EL_EXPLOSION;
3372 GfxElement[x][y] = artwork_element;
3374 ExplodePhase[x][y] = 1;
3375 ExplodeDelay[x][y] = last_phase;
3380 if (center_element == EL_YAMYAM)
3381 game.yamyam_content_nr =
3382 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3394 GfxFrame[x][y] = 0; /* restart explosion animation */
3396 last_phase = ExplodeDelay[x][y];
3398 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3402 /* activate this even in non-DEBUG version until cause for crash in
3403 getGraphicAnimationFrame() (see below) is found and eliminated */
3408 if (GfxElement[x][y] == EL_UNDEFINED)
3411 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3412 printf("Explode(): This should never happen!\n");
3415 GfxElement[x][y] = EL_EMPTY;
3419 border_element = Store2[x][y];
3420 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3421 border_element = StorePlayer[x][y];
3423 if (phase == element_info[border_element].ignition_delay ||
3424 phase == last_phase)
3426 boolean border_explosion = FALSE;
3428 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3429 !PLAYER_EXPLOSION_PROTECTED(x, y))
3431 KillPlayerUnlessExplosionProtected(x, y);
3432 border_explosion = TRUE;
3434 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3436 Feld[x][y] = Store2[x][y];
3439 border_explosion = TRUE;
3441 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3443 AmoebeUmwandeln(x, y);
3445 border_explosion = TRUE;
3448 /* if an element just explodes due to another explosion (chain-reaction),
3449 do not immediately end the new explosion when it was the last frame of
3450 the explosion (as it would be done in the following "if"-statement!) */
3451 if (border_explosion && phase == last_phase)
3455 if (phase == last_phase)
3459 element = Feld[x][y] = Store[x][y];
3460 Store[x][y] = Store2[x][y] = 0;
3461 GfxElement[x][y] = EL_UNDEFINED;
3463 /* player can escape from explosions and might therefore be still alive */
3464 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3465 element <= EL_PLAYER_IS_EXPLODING_4)
3467 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3468 int explosion_element = EL_PLAYER_1 + player_nr;
3469 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3470 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3472 if (level.use_explosion_element[player_nr])
3473 explosion_element = level.explosion_element[player_nr];
3475 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3476 element_info[explosion_element].content.e[xx][yy]);
3479 /* restore probably existing indestructible background element */
3480 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3481 element = Feld[x][y] = Back[x][y];
3484 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3485 GfxDir[x][y] = MV_NONE;
3486 ChangeDelay[x][y] = 0;
3487 ChangePage[x][y] = -1;
3489 #if USE_NEW_CUSTOM_VALUE
3490 CustomValue[x][y] = 0;
3493 InitField_WithBug2(x, y, FALSE);
3495 DrawLevelField(x, y);
3497 TestIfElementTouchesCustomElement(x, y);
3499 if (GFX_CRUMBLED(element))
3500 DrawLevelFieldCrumbledSandNeighbours(x, y);
3502 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3503 StorePlayer[x][y] = 0;
3505 if (ELEM_IS_PLAYER(element))
3506 RelocatePlayer(x, y, element);
3508 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3510 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3511 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3514 DrawLevelFieldCrumbledSand(x, y);
3516 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3518 DrawLevelElement(x, y, Back[x][y]);
3519 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3521 else if (IS_WALKABLE_UNDER(Back[x][y]))
3523 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3524 DrawLevelElementThruMask(x, y, Back[x][y]);
3526 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3527 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3531 void DynaExplode(int ex, int ey)
3534 int dynabomb_element = Feld[ex][ey];
3535 int dynabomb_size = 1;
3536 boolean dynabomb_xl = FALSE;
3537 struct PlayerInfo *player;
3538 static int xy[4][2] =
3546 if (IS_ACTIVE_BOMB(dynabomb_element))
3548 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3549 dynabomb_size = player->dynabomb_size;
3550 dynabomb_xl = player->dynabomb_xl;
3551 player->dynabombs_left++;
3554 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3556 for (i = 0; i < NUM_DIRECTIONS; i++)
3558 for (j = 1; j <= dynabomb_size; j++)
3560 int x = ex + j * xy[i][0];
3561 int y = ey + j * xy[i][1];
3564 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3567 element = Feld[x][y];
3569 /* do not restart explosions of fields with active bombs */
3570 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3573 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3575 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3576 !IS_DIGGABLE(element) && !dynabomb_xl)
3582 void Bang(int x, int y)
3584 int element = MovingOrBlocked2Element(x, y);
3585 int explosion_type = EX_TYPE_NORMAL;
3587 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3589 struct PlayerInfo *player = PLAYERINFO(x, y);
3591 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3592 player->element_nr);
3594 if (level.use_explosion_element[player->index_nr])
3596 int explosion_element = level.explosion_element[player->index_nr];
3598 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3599 explosion_type = EX_TYPE_CROSS;
3600 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3601 explosion_type = EX_TYPE_CENTER;
3609 case EL_BD_BUTTERFLY:
3612 case EL_DARK_YAMYAM:
3616 RaiseScoreElement(element);
3619 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3620 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3621 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3622 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3623 case EL_DYNABOMB_INCREASE_NUMBER:
3624 case EL_DYNABOMB_INCREASE_SIZE:
3625 case EL_DYNABOMB_INCREASE_POWER:
3626 explosion_type = EX_TYPE_DYNA;
3631 case EL_LAMP_ACTIVE:
3632 case EL_AMOEBA_TO_DIAMOND:
3633 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3634 explosion_type = EX_TYPE_CENTER;
3638 if (element_info[element].explosion_type == EXPLODES_CROSS)
3639 explosion_type = EX_TYPE_CROSS;
3640 else if (element_info[element].explosion_type == EXPLODES_1X1)
3641 explosion_type = EX_TYPE_CENTER;
3645 if (explosion_type == EX_TYPE_DYNA)
3648 Explode(x, y, EX_PHASE_START, explosion_type);
3650 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3653 void SplashAcid(int x, int y)
3655 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3656 (!IN_LEV_FIELD(x - 1, y - 2) ||
3657 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3658 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3660 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3661 (!IN_LEV_FIELD(x + 1, y - 2) ||
3662 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3663 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3665 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3668 static void InitBeltMovement()
3670 static int belt_base_element[4] =
3672 EL_CONVEYOR_BELT_1_LEFT,
3673 EL_CONVEYOR_BELT_2_LEFT,
3674 EL_CONVEYOR_BELT_3_LEFT,
3675 EL_CONVEYOR_BELT_4_LEFT
3677 static int belt_base_active_element[4] =
3679 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3680 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3681 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3682 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3687 /* set frame order for belt animation graphic according to belt direction */
3688 for (i = 0; i < NUM_BELTS; i++)
3692 for (j = 0; j < NUM_BELT_PARTS; j++)
3694 int element = belt_base_active_element[belt_nr] + j;
3695 int graphic = el2img(element);
3697 if (game.belt_dir[i] == MV_LEFT)
3698 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3700 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3705 SCAN_PLAYFIELD(x, y)
3707 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3710 int element = Feld[x][y];
3712 for (i = 0; i < NUM_BELTS; i++)
3714 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3716 int e_belt_nr = getBeltNrFromBeltElement(element);
3719 if (e_belt_nr == belt_nr)
3721 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3723 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3730 static void ToggleBeltSwitch(int x, int y)
3732 static int belt_base_element[4] =
3734 EL_CONVEYOR_BELT_1_LEFT,
3735 EL_CONVEYOR_BELT_2_LEFT,
3736 EL_CONVEYOR_BELT_3_LEFT,
3737 EL_CONVEYOR_BELT_4_LEFT
3739 static int belt_base_active_element[4] =
3741 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3742 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3743 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3744 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3746 static int belt_base_switch_element[4] =
3748 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3749 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3750 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3751 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3753 static int belt_move_dir[4] =
3761 int element = Feld[x][y];
3762 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3763 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3764 int belt_dir = belt_move_dir[belt_dir_nr];
3767 if (!IS_BELT_SWITCH(element))
3770 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3771 game.belt_dir[belt_nr] = belt_dir;
3773 if (belt_dir_nr == 3)
3776 /* set frame order for belt animation graphic according to belt direction */
3777 for (i = 0; i < NUM_BELT_PARTS; i++)
3779 int element = belt_base_active_element[belt_nr] + i;
3780 int graphic = el2img(element);
3782 if (belt_dir == MV_LEFT)
3783 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3785 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3789 SCAN_PLAYFIELD(xx, yy)
3791 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3794 int element = Feld[xx][yy];
3796 if (IS_BELT_SWITCH(element))
3798 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3800 if (e_belt_nr == belt_nr)
3802 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3803 DrawLevelField(xx, yy);
3806 else if (IS_BELT(element) && belt_dir != MV_NONE)
3808 int e_belt_nr = getBeltNrFromBeltElement(element);
3810 if (e_belt_nr == belt_nr)
3812 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3814 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3815 DrawLevelField(xx, yy);
3818 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3820 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3822 if (e_belt_nr == belt_nr)
3824 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3826 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3827 DrawLevelField(xx, yy);
3833 static void ToggleSwitchgateSwitch(int x, int y)
3837 game.switchgate_pos = !game.switchgate_pos;
3840 SCAN_PLAYFIELD(xx, yy)
3842 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3845 int element = Feld[xx][yy];
3847 if (element == EL_SWITCHGATE_SWITCH_UP ||
3848 element == EL_SWITCHGATE_SWITCH_DOWN)
3850 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3851 DrawLevelField(xx, yy);
3853 else if (element == EL_SWITCHGATE_OPEN ||
3854 element == EL_SWITCHGATE_OPENING)
3856 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3858 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3860 else if (element == EL_SWITCHGATE_CLOSED ||
3861 element == EL_SWITCHGATE_CLOSING)
3863 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3865 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3870 static int getInvisibleActiveFromInvisibleElement(int element)
3872 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3873 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3874 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3878 static int getInvisibleFromInvisibleActiveElement(int element)
3880 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3881 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3882 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3886 static void RedrawAllLightSwitchesAndInvisibleElements()
3891 SCAN_PLAYFIELD(x, y)
3893 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3896 int element = Feld[x][y];
3898 if (element == EL_LIGHT_SWITCH &&
3899 game.light_time_left > 0)
3901 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3902 DrawLevelField(x, y);
3904 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3905 game.light_time_left == 0)
3907 Feld[x][y] = EL_LIGHT_SWITCH;
3908 DrawLevelField(x, y);
3910 else if (element == EL_EMC_DRIPPER &&
3911 game.light_time_left > 0)
3913 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3914 DrawLevelField(x, y);
3916 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3917 game.light_time_left == 0)
3919 Feld[x][y] = EL_EMC_DRIPPER;
3920 DrawLevelField(x, y);
3922 else if (element == EL_INVISIBLE_STEELWALL ||
3923 element == EL_INVISIBLE_WALL ||
3924 element == EL_INVISIBLE_SAND)
3926 if (game.light_time_left > 0)
3927 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3929 DrawLevelField(x, y);
3931 /* uncrumble neighbour fields, if needed */
3932 if (element == EL_INVISIBLE_SAND)
3933 DrawLevelFieldCrumbledSandNeighbours(x, y);
3935 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3936 element == EL_INVISIBLE_WALL_ACTIVE ||
3937 element == EL_INVISIBLE_SAND_ACTIVE)
3939 if (game.light_time_left == 0)
3940 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3942 DrawLevelField(x, y);
3944 /* re-crumble neighbour fields, if needed */
3945 if (element == EL_INVISIBLE_SAND)
3946 DrawLevelFieldCrumbledSandNeighbours(x, y);
3951 static void RedrawAllInvisibleElementsForLenses()
3956 SCAN_PLAYFIELD(x, y)
3958 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3961 int element = Feld[x][y];
3963 if (element == EL_EMC_DRIPPER &&
3964 game.lenses_time_left > 0)
3966 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3967 DrawLevelField(x, y);
3969 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3970 game.lenses_time_left == 0)
3972 Feld[x][y] = EL_EMC_DRIPPER;
3973 DrawLevelField(x, y);
3975 else if (element == EL_INVISIBLE_STEELWALL ||
3976 element == EL_INVISIBLE_WALL ||
3977 element == EL_INVISIBLE_SAND)
3979 if (game.lenses_time_left > 0)
3980 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3982 DrawLevelField(x, y);
3984 /* uncrumble neighbour fields, if needed */
3985 if (element == EL_INVISIBLE_SAND)
3986 DrawLevelFieldCrumbledSandNeighbours(x, y);
3988 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3989 element == EL_INVISIBLE_WALL_ACTIVE ||
3990 element == EL_INVISIBLE_SAND_ACTIVE)
3992 if (game.lenses_time_left == 0)
3993 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3995 DrawLevelField(x, y);
3997 /* re-crumble neighbour fields, if needed */
3998 if (element == EL_INVISIBLE_SAND)
3999 DrawLevelFieldCrumbledSandNeighbours(x, y);
4004 static void RedrawAllInvisibleElementsForMagnifier()
4009 SCAN_PLAYFIELD(x, y)
4011 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4014 int element = Feld[x][y];
4016 if (element == EL_EMC_FAKE_GRASS &&
4017 game.magnify_time_left > 0)
4019 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4020 DrawLevelField(x, y);
4022 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4023 game.magnify_time_left == 0)
4025 Feld[x][y] = EL_EMC_FAKE_GRASS;
4026 DrawLevelField(x, y);
4028 else if (IS_GATE_GRAY(element) &&
4029 game.magnify_time_left > 0)
4031 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4032 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4033 IS_EM_GATE_GRAY(element) ?
4034 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4035 IS_EMC_GATE_GRAY(element) ?
4036 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4038 DrawLevelField(x, y);
4040 else if (IS_GATE_GRAY_ACTIVE(element) &&
4041 game.magnify_time_left == 0)
4043 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4044 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4045 IS_EM_GATE_GRAY_ACTIVE(element) ?
4046 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4047 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4048 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4050 DrawLevelField(x, y);
4055 static void ToggleLightSwitch(int x, int y)
4057 int element = Feld[x][y];
4059 game.light_time_left =
4060 (element == EL_LIGHT_SWITCH ?
4061 level.time_light * FRAMES_PER_SECOND : 0);
4063 RedrawAllLightSwitchesAndInvisibleElements();
4066 static void ActivateTimegateSwitch(int x, int y)
4070 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4073 SCAN_PLAYFIELD(xx, yy)
4075 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4078 int element = Feld[xx][yy];
4080 if (element == EL_TIMEGATE_CLOSED ||
4081 element == EL_TIMEGATE_CLOSING)
4083 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4084 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4088 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4090 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4091 DrawLevelField(xx, yy);
4097 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4100 void Impact(int x, int y)
4102 boolean last_line = (y == lev_fieldy - 1);
4103 boolean object_hit = FALSE;
4104 boolean impact = (last_line || object_hit);
4105 int element = Feld[x][y];
4106 int smashed = EL_STEELWALL;
4108 if (!last_line) /* check if element below was hit */
4110 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4113 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4114 MovDir[x][y + 1] != MV_DOWN ||
4115 MovPos[x][y + 1] <= TILEY / 2));
4117 /* do not smash moving elements that left the smashed field in time */
4118 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4119 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4122 #if USE_QUICKSAND_IMPACT_BUGFIX
4123 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4125 RemoveMovingField(x, y + 1);
4126 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4127 Feld[x][y + 2] = EL_ROCK;
4128 DrawLevelField(x, y + 2);
4135 smashed = MovingOrBlocked2Element(x, y + 1);
4137 impact = (last_line || object_hit);
4140 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4142 SplashAcid(x, y + 1);
4146 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4147 /* only reset graphic animation if graphic really changes after impact */
4149 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4151 ResetGfxAnimation(x, y);
4152 DrawLevelField(x, y);
4155 if (impact && CAN_EXPLODE_IMPACT(element))
4160 else if (impact && element == EL_PEARL)
4162 ResetGfxAnimation(x, y);
4164 Feld[x][y] = EL_PEARL_BREAKING;
4165 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4168 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4170 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4175 if (impact && element == EL_AMOEBA_DROP)
4177 if (object_hit && IS_PLAYER(x, y + 1))
4178 KillPlayerUnlessEnemyProtected(x, y + 1);
4179 else if (object_hit && smashed == EL_PENGUIN)
4183 Feld[x][y] = EL_AMOEBA_GROWING;
4184 Store[x][y] = EL_AMOEBA_WET;
4186 ResetRandomAnimationValue(x, y);
4191 if (object_hit) /* check which object was hit */
4193 if (CAN_PASS_MAGIC_WALL(element) &&
4194 (smashed == EL_MAGIC_WALL ||
4195 smashed == EL_BD_MAGIC_WALL))
4198 int activated_magic_wall =
4199 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4200 EL_BD_MAGIC_WALL_ACTIVE);
4202 /* activate magic wall / mill */
4204 SCAN_PLAYFIELD(xx, yy)
4206 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4208 if (Feld[xx][yy] == smashed)
4209 Feld[xx][yy] = activated_magic_wall;
4211 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4212 game.magic_wall_active = TRUE;
4214 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4215 SND_MAGIC_WALL_ACTIVATING :
4216 SND_BD_MAGIC_WALL_ACTIVATING));
4219 if (IS_PLAYER(x, y + 1))
4221 if (CAN_SMASH_PLAYER(element))
4223 KillPlayerUnlessEnemyProtected(x, y + 1);
4227 else if (smashed == EL_PENGUIN)
4229 if (CAN_SMASH_PLAYER(element))
4235 else if (element == EL_BD_DIAMOND)
4237 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4243 else if (((element == EL_SP_INFOTRON ||
4244 element == EL_SP_ZONK) &&
4245 (smashed == EL_SP_SNIKSNAK ||
4246 smashed == EL_SP_ELECTRON ||
4247 smashed == EL_SP_DISK_ORANGE)) ||
4248 (element == EL_SP_INFOTRON &&
4249 smashed == EL_SP_DISK_YELLOW))
4254 else if (CAN_SMASH_EVERYTHING(element))
4256 if (IS_CLASSIC_ENEMY(smashed) ||
4257 CAN_EXPLODE_SMASHED(smashed))
4262 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4264 if (smashed == EL_LAMP ||
4265 smashed == EL_LAMP_ACTIVE)
4270 else if (smashed == EL_NUT)
4272 Feld[x][y + 1] = EL_NUT_BREAKING;
4273 PlayLevelSound(x, y, SND_NUT_BREAKING);
4274 RaiseScoreElement(EL_NUT);
4277 else if (smashed == EL_PEARL)
4279 ResetGfxAnimation(x, y);
4281 Feld[x][y + 1] = EL_PEARL_BREAKING;
4282 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4285 else if (smashed == EL_DIAMOND)
4287 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4288 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4291 else if (IS_BELT_SWITCH(smashed))
4293 ToggleBeltSwitch(x, y + 1);
4295 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4296 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4298 ToggleSwitchgateSwitch(x, y + 1);
4300 else if (smashed == EL_LIGHT_SWITCH ||
4301 smashed == EL_LIGHT_SWITCH_ACTIVE)
4303 ToggleLightSwitch(x, y + 1);
4308 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4311 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4313 CheckElementChangeBySide(x, y + 1, smashed, element,
4314 CE_SWITCHED, CH_SIDE_TOP);
4315 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4321 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4326 /* play sound of magic wall / mill */
4328 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4329 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4331 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4332 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4333 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4334 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4339 /* play sound of object that hits the ground */
4340 if (last_line || object_hit)
4341 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4344 inline static void TurnRoundExt(int x, int y)
4356 { 0, 0 }, { 0, 0 }, { 0, 0 },
4361 int left, right, back;
4365 { MV_DOWN, MV_UP, MV_RIGHT },
4366 { MV_UP, MV_DOWN, MV_LEFT },
4368 { MV_LEFT, MV_RIGHT, MV_DOWN },
4372 { MV_RIGHT, MV_LEFT, MV_UP }
4375 int element = Feld[x][y];
4376 int move_pattern = element_info[element].move_pattern;
4378 int old_move_dir = MovDir[x][y];
4379 int left_dir = turn[old_move_dir].left;
4380 int right_dir = turn[old_move_dir].right;
4381 int back_dir = turn[old_move_dir].back;
4383 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4384 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4385 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4386 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4388 int left_x = x + left_dx, left_y = y + left_dy;
4389 int right_x = x + right_dx, right_y = y + right_dy;
4390 int move_x = x + move_dx, move_y = y + move_dy;
4394 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4396 TestIfBadThingTouchesOtherBadThing(x, y);
4398 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4399 MovDir[x][y] = right_dir;
4400 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4401 MovDir[x][y] = left_dir;
4403 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4405 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4408 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4410 TestIfBadThingTouchesOtherBadThing(x, y);
4412 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4413 MovDir[x][y] = left_dir;
4414 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4415 MovDir[x][y] = right_dir;
4417 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4419 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4422 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4424 TestIfBadThingTouchesOtherBadThing(x, y);
4426 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4427 MovDir[x][y] = left_dir;
4428 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4429 MovDir[x][y] = right_dir;
4431 if (MovDir[x][y] != old_move_dir)
4434 else if (element == EL_YAMYAM)
4436 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4437 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4439 if (can_turn_left && can_turn_right)
4440 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4441 else if (can_turn_left)
4442 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4443 else if (can_turn_right)
4444 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4446 MovDir[x][y] = back_dir;
4448 MovDelay[x][y] = 16 + 16 * RND(3);
4450 else if (element == EL_DARK_YAMYAM)
4452 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4454 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4457 if (can_turn_left && can_turn_right)
4458 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4459 else if (can_turn_left)
4460 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4461 else if (can_turn_right)
4462 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4464 MovDir[x][y] = back_dir;
4466 MovDelay[x][y] = 16 + 16 * RND(3);
4468 else if (element == EL_PACMAN)
4470 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4471 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4473 if (can_turn_left && can_turn_right)
4474 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4475 else if (can_turn_left)
4476 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4477 else if (can_turn_right)
4478 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4480 MovDir[x][y] = back_dir;
4482 MovDelay[x][y] = 6 + RND(40);
4484 else if (element == EL_PIG)
4486 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4487 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4488 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4489 boolean should_turn_left, should_turn_right, should_move_on;
4491 int rnd = RND(rnd_value);
4493 should_turn_left = (can_turn_left &&
4495 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4496 y + back_dy + left_dy)));
4497 should_turn_right = (can_turn_right &&
4499 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4500 y + back_dy + right_dy)));
4501 should_move_on = (can_move_on &&
4504 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4505 y + move_dy + left_dy) ||
4506 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4507 y + move_dy + right_dy)));
4509 if (should_turn_left || should_turn_right || should_move_on)
4511 if (should_turn_left && should_turn_right && should_move_on)
4512 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4513 rnd < 2 * rnd_value / 3 ? right_dir :
4515 else if (should_turn_left && should_turn_right)
4516 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4517 else if (should_turn_left && should_move_on)
4518 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4519 else if (should_turn_right && should_move_on)
4520 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4521 else if (should_turn_left)
4522 MovDir[x][y] = left_dir;
4523 else if (should_turn_right)
4524 MovDir[x][y] = right_dir;
4525 else if (should_move_on)
4526 MovDir[x][y] = old_move_dir;
4528 else if (can_move_on && rnd > rnd_value / 8)
4529 MovDir[x][y] = old_move_dir;
4530 else if (can_turn_left && can_turn_right)
4531 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4532 else if (can_turn_left && rnd > rnd_value / 8)
4533 MovDir[x][y] = left_dir;
4534 else if (can_turn_right && rnd > rnd_value/8)
4535 MovDir[x][y] = right_dir;
4537 MovDir[x][y] = back_dir;
4539 xx = x + move_xy[MovDir[x][y]].dx;
4540 yy = y + move_xy[MovDir[x][y]].dy;
4542 if (!IN_LEV_FIELD(xx, yy) ||
4543 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4544 MovDir[x][y] = old_move_dir;
4548 else if (element == EL_DRAGON)
4550 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4551 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4552 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4554 int rnd = RND(rnd_value);
4556 if (can_move_on && rnd > rnd_value / 8)
4557 MovDir[x][y] = old_move_dir;
4558 else if (can_turn_left && can_turn_right)
4559 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4560 else if (can_turn_left && rnd > rnd_value / 8)
4561 MovDir[x][y] = left_dir;
4562 else if (can_turn_right && rnd > rnd_value / 8)
4563 MovDir[x][y] = right_dir;
4565 MovDir[x][y] = back_dir;
4567 xx = x + move_xy[MovDir[x][y]].dx;
4568 yy = y + move_xy[MovDir[x][y]].dy;
4570 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4571 MovDir[x][y] = old_move_dir;
4575 else if (element == EL_MOLE)
4577 boolean can_move_on =
4578 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4579 IS_AMOEBOID(Feld[move_x][move_y]) ||
4580 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4583 boolean can_turn_left =
4584 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4585 IS_AMOEBOID(Feld[left_x][left_y])));
4587 boolean can_turn_right =
4588 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4589 IS_AMOEBOID(Feld[right_x][right_y])));
4591 if (can_turn_left && can_turn_right)
4592 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4593 else if (can_turn_left)
4594 MovDir[x][y] = left_dir;
4596 MovDir[x][y] = right_dir;
4599 if (MovDir[x][y] != old_move_dir)
4602 else if (element == EL_BALLOON)
4604 MovDir[x][y] = game.wind_direction;
4607 else if (element == EL_SPRING)
4609 if (MovDir[x][y] & MV_HORIZONTAL &&
4610 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4611 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4612 MovDir[x][y] = MV_NONE;
4616 else if (element == EL_ROBOT ||
4617 element == EL_SATELLITE ||
4618 element == EL_PENGUIN)
4620 int attr_x = -1, attr_y = -1;
4631 for (i = 0; i < MAX_PLAYERS; i++)
4633 struct PlayerInfo *player = &stored_player[i];
4634 int jx = player->jx, jy = player->jy;
4636 if (!player->active)
4640 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4648 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4649 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4650 game.engine_version < VERSION_IDENT(3,1,0,0)))
4656 if (element == EL_PENGUIN)
4659 static int xy[4][2] =
4667 for (i = 0; i < NUM_DIRECTIONS; i++)
4669 int ex = x + xy[i][0];
4670 int ey = y + xy[i][1];
4672 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4681 MovDir[x][y] = MV_NONE;
4683 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4684 else if (attr_x > x)
4685 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4687 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4688 else if (attr_y > y)
4689 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4691 if (element == EL_ROBOT)
4695 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4696 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4697 Moving2Blocked(x, y, &newx, &newy);
4699 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4700 MovDelay[x][y] = 8 + 8 * !RND(3);
4702 MovDelay[x][y] = 16;
4704 else if (element == EL_PENGUIN)
4710 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4712 boolean first_horiz = RND(2);
4713 int new_move_dir = MovDir[x][y];
4716 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4717 Moving2Blocked(x, y, &newx, &newy);
4719 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4723 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4724 Moving2Blocked(x, y, &newx, &newy);
4726 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4729 MovDir[x][y] = old_move_dir;
4733 else /* (element == EL_SATELLITE) */
4739 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4741 boolean first_horiz = RND(2);
4742 int new_move_dir = MovDir[x][y];
4745 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4746 Moving2Blocked(x, y, &newx, &newy);
4748 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4752 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4753 Moving2Blocked(x, y, &newx, &newy);
4755 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4758 MovDir[x][y] = old_move_dir;
4763 else if (move_pattern == MV_TURNING_LEFT ||
4764 move_pattern == MV_TURNING_RIGHT ||
4765 move_pattern == MV_TURNING_LEFT_RIGHT ||
4766 move_pattern == MV_TURNING_RIGHT_LEFT ||
4767 move_pattern == MV_TURNING_RANDOM ||
4768 move_pattern == MV_ALL_DIRECTIONS)
4770 boolean can_turn_left =
4771 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4772 boolean can_turn_right =
4773 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4775 if (element_info[element].move_stepsize == 0) /* "not moving" */
4778 if (move_pattern == MV_TURNING_LEFT)
4779 MovDir[x][y] = left_dir;
4780 else if (move_pattern == MV_TURNING_RIGHT)
4781 MovDir[x][y] = right_dir;
4782 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4783 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4784 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4785 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4786 else if (move_pattern == MV_TURNING_RANDOM)
4787 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4788 can_turn_right && !can_turn_left ? right_dir :
4789 RND(2) ? left_dir : right_dir);
4790 else if (can_turn_left && can_turn_right)
4791 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4792 else if (can_turn_left)
4793 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4794 else if (can_turn_right)
4795 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4797 MovDir[x][y] = back_dir;
4799 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4801 else if (move_pattern == MV_HORIZONTAL ||
4802 move_pattern == MV_VERTICAL)
4804 if (move_pattern & old_move_dir)
4805 MovDir[x][y] = back_dir;
4806 else if (move_pattern == MV_HORIZONTAL)
4807 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4808 else if (move_pattern == MV_VERTICAL)
4809 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4811 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4813 else if (move_pattern & MV_ANY_DIRECTION)
4815 MovDir[x][y] = move_pattern;
4816 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4818 else if (move_pattern & MV_WIND_DIRECTION)
4820 MovDir[x][y] = game.wind_direction;
4821 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4823 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4825 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4826 MovDir[x][y] = left_dir;
4827 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4828 MovDir[x][y] = right_dir;
4830 if (MovDir[x][y] != old_move_dir)
4831 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4833 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4835 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4836 MovDir[x][y] = right_dir;
4837 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4838 MovDir[x][y] = left_dir;
4840 if (MovDir[x][y] != old_move_dir)
4841 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4843 else if (move_pattern == MV_TOWARDS_PLAYER ||
4844 move_pattern == MV_AWAY_FROM_PLAYER)
4846 int attr_x = -1, attr_y = -1;
4848 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4859 for (i = 0; i < MAX_PLAYERS; i++)
4861 struct PlayerInfo *player = &stored_player[i];
4862 int jx = player->jx, jy = player->jy;
4864 if (!player->active)
4868 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4876 MovDir[x][y] = MV_NONE;
4878 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4879 else if (attr_x > x)
4880 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4882 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4883 else if (attr_y > y)
4884 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4886 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4888 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4890 boolean first_horiz = RND(2);
4891 int new_move_dir = MovDir[x][y];
4893 if (element_info[element].move_stepsize == 0) /* "not moving" */
4895 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4896 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4902 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4903 Moving2Blocked(x, y, &newx, &newy);
4905 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4909 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4910 Moving2Blocked(x, y, &newx, &newy);
4912 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4915 MovDir[x][y] = old_move_dir;
4918 else if (move_pattern == MV_WHEN_PUSHED ||
4919 move_pattern == MV_WHEN_DROPPED)
4921 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4922 MovDir[x][y] = MV_NONE;
4926 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4928 static int test_xy[7][2] =
4938 static int test_dir[7] =
4948 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4949 int move_preference = -1000000; /* start with very low preference */
4950 int new_move_dir = MV_NONE;
4951 int start_test = RND(4);
4954 for (i = 0; i < NUM_DIRECTIONS; i++)
4956 int move_dir = test_dir[start_test + i];
4957 int move_dir_preference;
4959 xx = x + test_xy[start_test + i][0];
4960 yy = y + test_xy[start_test + i][1];
4962 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4963 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4965 new_move_dir = move_dir;
4970 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4973 move_dir_preference = -1 * RunnerVisit[xx][yy];
4974 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4975 move_dir_preference = PlayerVisit[xx][yy];
4977 if (move_dir_preference > move_preference)
4979 /* prefer field that has not been visited for the longest time */
4980 move_preference = move_dir_preference;
4981 new_move_dir = move_dir;
4983 else if (move_dir_preference == move_preference &&
4984 move_dir == old_move_dir)
4986 /* prefer last direction when all directions are preferred equally */
4987 move_preference = move_dir_preference;
4988 new_move_dir = move_dir;
4992 MovDir[x][y] = new_move_dir;
4993 if (old_move_dir != new_move_dir)
4994 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4998 static void TurnRound(int x, int y)
5000 int direction = MovDir[x][y];
5004 GfxDir[x][y] = MovDir[x][y];
5006 if (direction != MovDir[x][y])
5010 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5013 static boolean JustBeingPushed(int x, int y)
5017 for (i = 0; i < MAX_PLAYERS; i++)
5019 struct PlayerInfo *player = &stored_player[i];
5021 if (player->active && player->is_pushing && player->MovPos)
5023 int next_jx = player->jx + (player->jx - player->last_jx);
5024 int next_jy = player->jy + (player->jy - player->last_jy);
5026 if (x == next_jx && y == next_jy)
5034 void StartMoving(int x, int y)
5036 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5037 int element = Feld[x][y];
5042 if (MovDelay[x][y] == 0)
5043 GfxAction[x][y] = ACTION_DEFAULT;
5045 if (CAN_FALL(element) && y < lev_fieldy - 1)
5047 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5048 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5049 if (JustBeingPushed(x, y))
5052 if (element == EL_QUICKSAND_FULL)
5054 if (IS_FREE(x, y + 1))
5056 InitMovingField(x, y, MV_DOWN);
5057 started_moving = TRUE;
5059 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5060 Store[x][y] = EL_ROCK;
5062 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5064 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5066 if (!MovDelay[x][y])
5067 MovDelay[x][y] = TILEY + 1;
5076 Feld[x][y] = EL_QUICKSAND_EMPTY;
5077 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5078 Store[x][y + 1] = Store[x][y];
5081 PlayLevelSoundAction(x, y, ACTION_FILLING);
5084 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5085 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5087 InitMovingField(x, y, MV_DOWN);
5088 started_moving = TRUE;
5090 Feld[x][y] = EL_QUICKSAND_FILLING;
5091 Store[x][y] = element;
5093 PlayLevelSoundAction(x, y, ACTION_FILLING);
5095 else if (element == EL_MAGIC_WALL_FULL)
5097 if (IS_FREE(x, y + 1))
5099 InitMovingField(x, y, MV_DOWN);
5100 started_moving = TRUE;
5102 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5103 Store[x][y] = EL_CHANGED(Store[x][y]);
5105 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5107 if (!MovDelay[x][y])
5108 MovDelay[x][y] = TILEY/4 + 1;
5117 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5118 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5119 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5123 else if (element == EL_BD_MAGIC_WALL_FULL)
5125 if (IS_FREE(x, y + 1))
5127 InitMovingField(x, y, MV_DOWN);
5128 started_moving = TRUE;
5130 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5131 Store[x][y] = EL_CHANGED2(Store[x][y]);
5133 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5135 if (!MovDelay[x][y])
5136 MovDelay[x][y] = TILEY/4 + 1;
5145 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5146 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5147 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5151 else if (CAN_PASS_MAGIC_WALL(element) &&
5152 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5153 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5155 InitMovingField(x, y, MV_DOWN);
5156 started_moving = TRUE;
5159 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5160 EL_BD_MAGIC_WALL_FILLING);
5161 Store[x][y] = element;
5163 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5165 SplashAcid(x, y + 1);
5167 InitMovingField(x, y, MV_DOWN);
5168 started_moving = TRUE;
5170 Store[x][y] = EL_ACID;
5172 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5173 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5175 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5176 CAN_FALL(element) && WasJustFalling[x][y] &&
5177 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5179 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5180 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5181 (Feld[x][y + 1] == EL_BLOCKED)))
5183 /* this is needed for a special case not covered by calling "Impact()"
5184 from "ContinueMoving()": if an element moves to a tile directly below
5185 another element which was just falling on that tile (which was empty
5186 in the previous frame), the falling element above would just stop
5187 instead of smashing the element below (in previous version, the above
5188 element was just checked for "moving" instead of "falling", resulting
5189 in incorrect smashes caused by horizontal movement of the above
5190 element; also, the case of the player being the element to smash was
5191 simply not covered here... :-/ ) */
5193 CheckCollision[x][y] = 0;
5197 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5199 if (MovDir[x][y] == MV_NONE)
5201 InitMovingField(x, y, MV_DOWN);
5202 started_moving = TRUE;
5205 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5207 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5208 MovDir[x][y] = MV_DOWN;
5210 InitMovingField(x, y, MV_DOWN);
5211 started_moving = TRUE;
5213 else if (element == EL_AMOEBA_DROP)
5215 Feld[x][y] = EL_AMOEBA_GROWING;
5216 Store[x][y] = EL_AMOEBA_WET;
5218 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5219 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5220 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5221 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5223 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5224 (IS_FREE(x - 1, y + 1) ||
5225 Feld[x - 1][y + 1] == EL_ACID));
5226 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5227 (IS_FREE(x + 1, y + 1) ||
5228 Feld[x + 1][y + 1] == EL_ACID));
5229 boolean can_fall_any = (can_fall_left || can_fall_right);
5230 boolean can_fall_both = (can_fall_left && can_fall_right);
5231 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5233 #if USE_NEW_ALL_SLIPPERY
5234 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5236 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5237 can_fall_right = FALSE;
5238 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5239 can_fall_left = FALSE;
5240 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5241 can_fall_right = FALSE;
5242 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5243 can_fall_left = FALSE;
5245 can_fall_any = (can_fall_left || can_fall_right);
5246 can_fall_both = FALSE;
5249 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5251 if (slippery_type == SLIPPERY_ONLY_LEFT)
5252 can_fall_right = FALSE;
5253 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5254 can_fall_left = FALSE;
5255 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5256 can_fall_right = FALSE;
5257 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5258 can_fall_left = FALSE;
5260 can_fall_any = (can_fall_left || can_fall_right);
5261 can_fall_both = (can_fall_left && can_fall_right);
5265 #if USE_NEW_ALL_SLIPPERY
5267 #if USE_NEW_SP_SLIPPERY
5268 /* !!! better use the same properties as for custom elements here !!! */
5269 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5270 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5272 can_fall_right = FALSE; /* slip down on left side */
5273 can_fall_both = FALSE;
5278 #if USE_NEW_ALL_SLIPPERY
5281 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5282 can_fall_right = FALSE; /* slip down on left side */
5284 can_fall_left = !(can_fall_right = RND(2));
5286 can_fall_both = FALSE;
5291 if (game.emulation == EMU_BOULDERDASH ||
5292 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5293 can_fall_right = FALSE; /* slip down on left side */
5295 can_fall_left = !(can_fall_right = RND(2));
5297 can_fall_both = FALSE;
5303 /* if not determined otherwise, prefer left side for slipping down */
5304 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5305 started_moving = TRUE;
5309 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5311 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5314 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5315 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5316 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5317 int belt_dir = game.belt_dir[belt_nr];
5319 if ((belt_dir == MV_LEFT && left_is_free) ||
5320 (belt_dir == MV_RIGHT && right_is_free))
5322 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5324 InitMovingField(x, y, belt_dir);
5325 started_moving = TRUE;
5327 Pushed[x][y] = TRUE;
5328 Pushed[nextx][y] = TRUE;
5330 GfxAction[x][y] = ACTION_DEFAULT;
5334 MovDir[x][y] = 0; /* if element was moving, stop it */
5339 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5341 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5343 if (CAN_MOVE(element) && !started_moving)
5346 int move_pattern = element_info[element].move_pattern;
5351 if (MovDir[x][y] == MV_NONE)
5353 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5354 x, y, element, element_info[element].token_name);
5355 printf("StartMoving(): This should never happen!\n");
5360 Moving2Blocked(x, y, &newx, &newy);
5362 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5365 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5366 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5368 WasJustMoving[x][y] = 0;
5369 CheckCollision[x][y] = 0;
5371 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5373 if (Feld[x][y] != element) /* element has changed */
5377 if (!MovDelay[x][y]) /* start new movement phase */
5379 /* all objects that can change their move direction after each step
5380 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5382 if (element != EL_YAMYAM &&
5383 element != EL_DARK_YAMYAM &&
5384 element != EL_PACMAN &&
5385 !(move_pattern & MV_ANY_DIRECTION) &&
5386 move_pattern != MV_TURNING_LEFT &&
5387 move_pattern != MV_TURNING_RIGHT &&
5388 move_pattern != MV_TURNING_LEFT_RIGHT &&
5389 move_pattern != MV_TURNING_RIGHT_LEFT &&
5390 move_pattern != MV_TURNING_RANDOM)
5394 if (MovDelay[x][y] && (element == EL_BUG ||
5395 element == EL_SPACESHIP ||
5396 element == EL_SP_SNIKSNAK ||
5397 element == EL_SP_ELECTRON ||
5398 element == EL_MOLE))
5399 DrawLevelField(x, y);
5403 if (MovDelay[x][y]) /* wait some time before next movement */
5407 if (element == EL_ROBOT ||
5408 element == EL_YAMYAM ||
5409 element == EL_DARK_YAMYAM)
5411 DrawLevelElementAnimationIfNeeded(x, y, element);
5412 PlayLevelSoundAction(x, y, ACTION_WAITING);
5414 else if (element == EL_SP_ELECTRON)
5415 DrawLevelElementAnimationIfNeeded(x, y, element);
5416 else if (element == EL_DRAGON)
5419 int dir = MovDir[x][y];
5420 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5421 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5422 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5423 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5424 dir == MV_UP ? IMG_FLAMES_1_UP :
5425 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5426 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5428 GfxAction[x][y] = ACTION_ATTACKING;
5430 if (IS_PLAYER(x, y))
5431 DrawPlayerField(x, y);
5433 DrawLevelField(x, y);
5435 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5437 for (i = 1; i <= 3; i++)
5439 int xx = x + i * dx;
5440 int yy = y + i * dy;
5441 int sx = SCREENX(xx);
5442 int sy = SCREENY(yy);
5443 int flame_graphic = graphic + (i - 1);
5445 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5450 int flamed = MovingOrBlocked2Element(xx, yy);
5454 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5456 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5457 RemoveMovingField(xx, yy);
5459 RemoveField(xx, yy);
5461 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5464 RemoveMovingField(xx, yy);
5467 ChangeDelay[xx][yy] = 0;
5469 Feld[xx][yy] = EL_FLAMES;
5471 if (IN_SCR_FIELD(sx, sy))
5473 DrawLevelFieldCrumbledSand(xx, yy);
5474 DrawGraphic(sx, sy, flame_graphic, frame);
5479 if (Feld[xx][yy] == EL_FLAMES)
5480 Feld[xx][yy] = EL_EMPTY;
5481 DrawLevelField(xx, yy);
5486 if (MovDelay[x][y]) /* element still has to wait some time */
5488 PlayLevelSoundAction(x, y, ACTION_WAITING);
5494 /* now make next step */
5496 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5498 if (DONT_COLLIDE_WITH(element) &&
5499 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5500 !PLAYER_ENEMY_PROTECTED(newx, newy))
5502 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5507 else if (CAN_MOVE_INTO_ACID(element) &&
5508 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5509 (MovDir[x][y] == MV_DOWN ||
5510 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5512 SplashAcid(newx, newy);
5513 Store[x][y] = EL_ACID;
5515 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5517 if (Feld[newx][newy] == EL_EXIT_OPEN)
5520 DrawLevelField(x, y);
5522 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5523 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5524 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5526 local_player->friends_still_needed--;
5527 if (!local_player->friends_still_needed &&
5528 !local_player->GameOver && AllPlayersGone)
5529 local_player->LevelSolved = local_player->GameOver = TRUE;
5533 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5535 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5536 DrawLevelField(newx, newy);
5538 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5540 else if (!IS_FREE(newx, newy))
5542 GfxAction[x][y] = ACTION_WAITING;
5544 if (IS_PLAYER(x, y))
5545 DrawPlayerField(x, y);
5547 DrawLevelField(x, y);
5552 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5554 if (IS_FOOD_PIG(Feld[newx][newy]))
5556 if (IS_MOVING(newx, newy))
5557 RemoveMovingField(newx, newy);
5560 Feld[newx][newy] = EL_EMPTY;
5561 DrawLevelField(newx, newy);
5564 PlayLevelSound(x, y, SND_PIG_DIGGING);
5566 else if (!IS_FREE(newx, newy))
5568 if (IS_PLAYER(x, y))
5569 DrawPlayerField(x, y);
5571 DrawLevelField(x, y);
5576 else if (IS_CUSTOM_ELEMENT(element) &&
5577 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5579 int new_element = Feld[newx][newy];
5581 if (!IS_FREE(newx, newy))
5583 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5584 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5587 /* no element can dig solid indestructible elements */
5588 if (IS_INDESTRUCTIBLE(new_element) &&
5589 !IS_DIGGABLE(new_element) &&
5590 !IS_COLLECTIBLE(new_element))
5593 if (AmoebaNr[newx][newy] &&
5594 (new_element == EL_AMOEBA_FULL ||
5595 new_element == EL_BD_AMOEBA ||
5596 new_element == EL_AMOEBA_GROWING))
5598 AmoebaCnt[AmoebaNr[newx][newy]]--;
5599 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5602 if (IS_MOVING(newx, newy))
5603 RemoveMovingField(newx, newy);
5606 RemoveField(newx, newy);
5607 DrawLevelField(newx, newy);
5610 /* if digged element was about to explode, prevent the explosion */
5611 ExplodeField[newx][newy] = EX_TYPE_NONE;
5613 PlayLevelSoundAction(x, y, action);
5616 Store[newx][newy] = EL_EMPTY;
5618 /* this makes it possible to leave the removed element again */
5619 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5620 Store[newx][newy] = new_element;
5622 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5624 int move_leave_element = element_info[element].move_leave_element;
5626 /* this makes it possible to leave the removed element again */
5627 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5628 new_element : move_leave_element);
5632 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5634 RunnerVisit[x][y] = FrameCounter;
5635 PlayerVisit[x][y] /= 8; /* expire player visit path */
5638 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5640 if (!IS_FREE(newx, newy))
5642 if (IS_PLAYER(x, y))
5643 DrawPlayerField(x, y);
5645 DrawLevelField(x, y);
5651 boolean wanna_flame = !RND(10);
5652 int dx = newx - x, dy = newy - y;
5653 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5654 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5655 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5656 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5657 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5658 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5661 IS_CLASSIC_ENEMY(element1) ||
5662 IS_CLASSIC_ENEMY(element2)) &&
5663 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5664 element1 != EL_FLAMES && element2 != EL_FLAMES)
5666 ResetGfxAnimation(x, y);
5667 GfxAction[x][y] = ACTION_ATTACKING;
5669 if (IS_PLAYER(x, y))
5670 DrawPlayerField(x, y);
5672 DrawLevelField(x, y);
5674 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5676 MovDelay[x][y] = 50;
5680 RemoveField(newx, newy);
5682 Feld[newx][newy] = EL_FLAMES;
5683 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5686 RemoveField(newx1, newy1);
5688 Feld[newx1][newy1] = EL_FLAMES;
5690 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5693 RemoveField(newx2, newy2);
5695 Feld[newx2][newy2] = EL_FLAMES;
5702 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5703 Feld[newx][newy] == EL_DIAMOND)
5705 if (IS_MOVING(newx, newy))
5706 RemoveMovingField(newx, newy);
5709 Feld[newx][newy] = EL_EMPTY;
5710 DrawLevelField(newx, newy);
5713 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5715 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5716 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5718 if (AmoebaNr[newx][newy])
5720 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5721 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5722 Feld[newx][newy] == EL_BD_AMOEBA)
5723 AmoebaCnt[AmoebaNr[newx][newy]]--;
5728 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5730 RemoveMovingField(newx, newy);
5733 if (IS_MOVING(newx, newy))
5735 RemoveMovingField(newx, newy);
5740 Feld[newx][newy] = EL_EMPTY;
5741 DrawLevelField(newx, newy);
5744 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5746 else if ((element == EL_PACMAN || element == EL_MOLE)
5747 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5749 if (AmoebaNr[newx][newy])
5751 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5752 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5753 Feld[newx][newy] == EL_BD_AMOEBA)
5754 AmoebaCnt[AmoebaNr[newx][newy]]--;
5757 if (element == EL_MOLE)
5759 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5760 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5762 ResetGfxAnimation(x, y);
5763 GfxAction[x][y] = ACTION_DIGGING;
5764 DrawLevelField(x, y);
5766 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5768 return; /* wait for shrinking amoeba */
5770 else /* element == EL_PACMAN */
5772 Feld[newx][newy] = EL_EMPTY;
5773 DrawLevelField(newx, newy);
5774 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5777 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5778 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5779 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5781 /* wait for shrinking amoeba to completely disappear */
5784 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5786 /* object was running against a wall */
5791 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5792 if (move_pattern & MV_ANY_DIRECTION &&
5793 move_pattern == MovDir[x][y])
5795 int blocking_element =
5796 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5798 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5801 element = Feld[x][y]; /* element might have changed */
5805 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5806 DrawLevelElementAnimation(x, y, element);
5808 if (DONT_TOUCH(element))
5809 TestIfBadThingTouchesPlayer(x, y);
5814 InitMovingField(x, y, MovDir[x][y]);
5816 PlayLevelSoundAction(x, y, ACTION_MOVING);
5820 ContinueMoving(x, y);
5823 void ContinueMoving(int x, int y)
5825 int element = Feld[x][y];
5826 struct ElementInfo *ei = &element_info[element];
5827 int direction = MovDir[x][y];
5828 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5829 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5830 int newx = x + dx, newy = y + dy;
5831 int stored = Store[x][y];
5832 int stored_new = Store[newx][newy];
5833 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5834 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5835 boolean last_line = (newy == lev_fieldy - 1);
5837 MovPos[x][y] += getElementMoveStepsize(x, y);
5839 if (pushed_by_player) /* special case: moving object pushed by player */
5840 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5842 if (ABS(MovPos[x][y]) < TILEX)
5844 DrawLevelField(x, y);
5846 return; /* element is still moving */
5849 /* element reached destination field */
5851 Feld[x][y] = EL_EMPTY;
5852 Feld[newx][newy] = element;
5853 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5855 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5857 element = Feld[newx][newy] = EL_ACID;
5859 else if (element == EL_MOLE)
5861 Feld[x][y] = EL_SAND;
5863 DrawLevelFieldCrumbledSandNeighbours(x, y);
5865 else if (element == EL_QUICKSAND_FILLING)
5867 element = Feld[newx][newy] = get_next_element(element);
5868 Store[newx][newy] = Store[x][y];
5870 else if (element == EL_QUICKSAND_EMPTYING)
5872 Feld[x][y] = get_next_element(element);
5873 element = Feld[newx][newy] = Store[x][y];
5875 else if (element == EL_MAGIC_WALL_FILLING)
5877 element = Feld[newx][newy] = get_next_element(element);
5878 if (!game.magic_wall_active)
5879 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5880 Store[newx][newy] = Store[x][y];
5882 else if (element == EL_MAGIC_WALL_EMPTYING)
5884 Feld[x][y] = get_next_element(element);
5885 if (!game.magic_wall_active)
5886 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5887 element = Feld[newx][newy] = Store[x][y];
5889 #if USE_NEW_CUSTOM_VALUE
5890 InitField(newx, newy, FALSE);
5893 else if (element == EL_BD_MAGIC_WALL_FILLING)
5895 element = Feld[newx][newy] = get_next_element(element);
5896 if (!game.magic_wall_active)
5897 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5898 Store[newx][newy] = Store[x][y];
5900 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5902 Feld[x][y] = get_next_element(element);
5903 if (!game.magic_wall_active)
5904 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5905 element = Feld[newx][newy] = Store[x][y];
5907 #if USE_NEW_CUSTOM_VALUE
5908 InitField(newx, newy, FALSE);
5911 else if (element == EL_AMOEBA_DROPPING)
5913 Feld[x][y] = get_next_element(element);
5914 element = Feld[newx][newy] = Store[x][y];
5916 else if (element == EL_SOKOBAN_OBJECT)
5919 Feld[x][y] = Back[x][y];
5921 if (Back[newx][newy])
5922 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5924 Back[x][y] = Back[newx][newy] = 0;
5927 Store[x][y] = EL_EMPTY;
5932 MovDelay[newx][newy] = 0;
5935 if (CAN_CHANGE_OR_HAS_ACTION(element))
5937 if (CAN_CHANGE(element))
5940 /* copy element change control values to new field */
5941 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5942 ChangePage[newx][newy] = ChangePage[x][y];
5943 ChangeCount[newx][newy] = ChangeCount[x][y];
5944 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5947 #if USE_NEW_CUSTOM_VALUE
5948 CustomValue[newx][newy] = CustomValue[x][y];
5954 #if USE_NEW_CUSTOM_VALUE
5955 CustomValue[newx][newy] = CustomValue[x][y];
5959 ChangeDelay[x][y] = 0;
5960 ChangePage[x][y] = -1;
5961 ChangeCount[x][y] = 0;
5962 ChangeEvent[x][y] = -1;
5964 #if USE_NEW_CUSTOM_VALUE
5965 CustomValue[x][y] = 0;
5968 /* copy animation control values to new field */
5969 GfxFrame[newx][newy] = GfxFrame[x][y];
5970 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5971 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5972 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5974 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5976 /* some elements can leave other elements behind after moving */
5977 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5978 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5979 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5981 int move_leave_element = ei->move_leave_element;
5984 /* this makes it possible to leave the removed element again */
5985 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
5986 move_leave_element = stored;
5988 /* this makes it possible to leave the removed element again */
5989 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5990 ei->move_leave_element == EL_TRIGGER_ELEMENT)
5991 move_leave_element = stored;
5994 Feld[x][y] = move_leave_element;
5996 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5997 MovDir[x][y] = direction;
5999 InitField(x, y, FALSE);
6001 if (GFX_CRUMBLED(Feld[x][y]))
6002 DrawLevelFieldCrumbledSandNeighbours(x, y);
6004 if (ELEM_IS_PLAYER(move_leave_element))
6005 RelocatePlayer(x, y, move_leave_element);
6008 /* do this after checking for left-behind element */
6009 ResetGfxAnimation(x, y); /* reset animation values for old field */
6011 if (!CAN_MOVE(element) ||
6012 (CAN_FALL(element) && direction == MV_DOWN &&
6013 (element == EL_SPRING ||
6014 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6015 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6016 GfxDir[x][y] = MovDir[newx][newy] = 0;
6018 DrawLevelField(x, y);
6019 DrawLevelField(newx, newy);
6021 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6023 /* prevent pushed element from moving on in pushed direction */
6024 if (pushed_by_player && CAN_MOVE(element) &&
6025 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6026 !(element_info[element].move_pattern & direction))
6027 TurnRound(newx, newy);
6029 /* prevent elements on conveyor belt from moving on in last direction */
6030 if (pushed_by_conveyor && CAN_FALL(element) &&
6031 direction & MV_HORIZONTAL)
6032 MovDir[newx][newy] = 0;
6034 if (!pushed_by_player)
6036 int nextx = newx + dx, nexty = newy + dy;
6037 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6039 WasJustMoving[newx][newy] = 3;
6041 if (CAN_FALL(element) && direction == MV_DOWN)
6042 WasJustFalling[newx][newy] = 3;
6044 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6045 CheckCollision[newx][newy] = 2;
6048 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6050 TestIfBadThingTouchesPlayer(newx, newy);
6051 TestIfBadThingTouchesFriend(newx, newy);
6053 if (!IS_CUSTOM_ELEMENT(element))
6054 TestIfBadThingTouchesOtherBadThing(newx, newy);
6056 else if (element == EL_PENGUIN)
6057 TestIfFriendTouchesBadThing(newx, newy);
6059 /* give the player one last chance (one more frame) to move away */
6060 if (CAN_FALL(element) && direction == MV_DOWN &&
6061 (last_line || (!IS_FREE(x, newy + 1) &&
6062 (!IS_PLAYER(x, newy + 1) ||
6063 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6066 if (pushed_by_player && !game.use_change_when_pushing_bug)
6068 int push_side = MV_DIR_OPPOSITE(direction);
6069 struct PlayerInfo *player = PLAYERINFO(x, y);
6071 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6072 player->index_bit, push_side);
6073 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6074 player->index_bit, push_side);
6077 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6079 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6081 TestIfElementHitsCustomElement(newx, newy, direction);
6082 TestIfPlayerTouchesCustomElement(newx, newy);
6083 TestIfElementTouchesCustomElement(newx, newy);
6086 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6087 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6088 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6089 MV_DIR_OPPOSITE(direction));
6093 int AmoebeNachbarNr(int ax, int ay)
6096 int element = Feld[ax][ay];
6098 static int xy[4][2] =
6106 for (i = 0; i < NUM_DIRECTIONS; i++)
6108 int x = ax + xy[i][0];
6109 int y = ay + xy[i][1];
6111 if (!IN_LEV_FIELD(x, y))
6114 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6115 group_nr = AmoebaNr[x][y];
6121 void AmoebenVereinigen(int ax, int ay)
6123 int i, x, y, xx, yy;
6124 int new_group_nr = AmoebaNr[ax][ay];
6125 static int xy[4][2] =
6133 if (new_group_nr == 0)
6136 for (i = 0; i < NUM_DIRECTIONS; i++)
6141 if (!IN_LEV_FIELD(x, y))
6144 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6145 Feld[x][y] == EL_BD_AMOEBA ||
6146 Feld[x][y] == EL_AMOEBA_DEAD) &&
6147 AmoebaNr[x][y] != new_group_nr)
6149 int old_group_nr = AmoebaNr[x][y];
6151 if (old_group_nr == 0)
6154 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6155 AmoebaCnt[old_group_nr] = 0;
6156 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6157 AmoebaCnt2[old_group_nr] = 0;
6160 SCAN_PLAYFIELD(xx, yy)
6162 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6165 if (AmoebaNr[xx][yy] == old_group_nr)
6166 AmoebaNr[xx][yy] = new_group_nr;
6172 void AmoebeUmwandeln(int ax, int ay)
6176 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6178 int group_nr = AmoebaNr[ax][ay];
6183 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6184 printf("AmoebeUmwandeln(): This should never happen!\n");
6190 SCAN_PLAYFIELD(x, y)
6192 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6195 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6198 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6202 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6203 SND_AMOEBA_TURNING_TO_GEM :
6204 SND_AMOEBA_TURNING_TO_ROCK));
6209 static int xy[4][2] =
6217 for (i = 0; i < NUM_DIRECTIONS; i++)
6222 if (!IN_LEV_FIELD(x, y))
6225 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6227 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6228 SND_AMOEBA_TURNING_TO_GEM :
6229 SND_AMOEBA_TURNING_TO_ROCK));
6236 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6239 int group_nr = AmoebaNr[ax][ay];
6240 boolean done = FALSE;
6245 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6246 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6252 SCAN_PLAYFIELD(x, y)
6254 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6257 if (AmoebaNr[x][y] == group_nr &&
6258 (Feld[x][y] == EL_AMOEBA_DEAD ||
6259 Feld[x][y] == EL_BD_AMOEBA ||
6260 Feld[x][y] == EL_AMOEBA_GROWING))
6263 Feld[x][y] = new_element;
6264 InitField(x, y, FALSE);
6265 DrawLevelField(x, y);
6271 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6272 SND_BD_AMOEBA_TURNING_TO_ROCK :
6273 SND_BD_AMOEBA_TURNING_TO_GEM));
6276 void AmoebeWaechst(int x, int y)
6278 static unsigned long sound_delay = 0;
6279 static unsigned long sound_delay_value = 0;
6281 if (!MovDelay[x][y]) /* start new growing cycle */
6285 if (DelayReached(&sound_delay, sound_delay_value))
6287 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6288 sound_delay_value = 30;
6292 if (MovDelay[x][y]) /* wait some time before growing bigger */
6295 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6297 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6298 6 - MovDelay[x][y]);
6300 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6303 if (!MovDelay[x][y])
6305 Feld[x][y] = Store[x][y];
6307 DrawLevelField(x, y);
6312 void AmoebaDisappearing(int x, int y)
6314 static unsigned long sound_delay = 0;
6315 static unsigned long sound_delay_value = 0;
6317 if (!MovDelay[x][y]) /* start new shrinking cycle */
6321 if (DelayReached(&sound_delay, sound_delay_value))
6322 sound_delay_value = 30;
6325 if (MovDelay[x][y]) /* wait some time before shrinking */
6328 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6330 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6331 6 - MovDelay[x][y]);
6333 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6336 if (!MovDelay[x][y])
6338 Feld[x][y] = EL_EMPTY;
6339 DrawLevelField(x, y);
6341 /* don't let mole enter this field in this cycle;
6342 (give priority to objects falling to this field from above) */
6348 void AmoebeAbleger(int ax, int ay)
6351 int element = Feld[ax][ay];
6352 int graphic = el2img(element);
6353 int newax = ax, neway = ay;
6354 static int xy[4][2] =
6362 if (!level.amoeba_speed)
6364 Feld[ax][ay] = EL_AMOEBA_DEAD;
6365 DrawLevelField(ax, ay);
6369 if (IS_ANIMATED(graphic))
6370 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6372 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6373 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6375 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6378 if (MovDelay[ax][ay])
6382 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6385 int x = ax + xy[start][0];
6386 int y = ay + xy[start][1];
6388 if (!IN_LEV_FIELD(x, y))
6391 if (IS_FREE(x, y) ||
6392 CAN_GROW_INTO(Feld[x][y]) ||
6393 Feld[x][y] == EL_QUICKSAND_EMPTY)
6399 if (newax == ax && neway == ay)
6402 else /* normal or "filled" (BD style) amoeba */
6405 boolean waiting_for_player = FALSE;
6407 for (i = 0; i < NUM_DIRECTIONS; i++)
6409 int j = (start + i) % 4;
6410 int x = ax + xy[j][0];
6411 int y = ay + xy[j][1];
6413 if (!IN_LEV_FIELD(x, y))
6416 if (IS_FREE(x, y) ||
6417 CAN_GROW_INTO(Feld[x][y]) ||
6418 Feld[x][y] == EL_QUICKSAND_EMPTY)
6424 else if (IS_PLAYER(x, y))
6425 waiting_for_player = TRUE;
6428 if (newax == ax && neway == ay) /* amoeba cannot grow */
6430 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6432 Feld[ax][ay] = EL_AMOEBA_DEAD;
6433 DrawLevelField(ax, ay);
6434 AmoebaCnt[AmoebaNr[ax][ay]]--;
6436 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6438 if (element == EL_AMOEBA_FULL)
6439 AmoebeUmwandeln(ax, ay);
6440 else if (element == EL_BD_AMOEBA)
6441 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6446 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6448 /* amoeba gets larger by growing in some direction */
6450 int new_group_nr = AmoebaNr[ax][ay];
6453 if (new_group_nr == 0)
6455 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6456 printf("AmoebeAbleger(): This should never happen!\n");
6461 AmoebaNr[newax][neway] = new_group_nr;
6462 AmoebaCnt[new_group_nr]++;
6463 AmoebaCnt2[new_group_nr]++;
6465 /* if amoeba touches other amoeba(s) after growing, unify them */
6466 AmoebenVereinigen(newax, neway);
6468 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6470 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6476 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6477 (neway == lev_fieldy - 1 && newax != ax))
6479 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6480 Store[newax][neway] = element;
6482 else if (neway == ay)
6484 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6486 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6490 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6491 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6492 Store[ax][ay] = EL_AMOEBA_DROP;
6493 ContinueMoving(ax, ay);
6497 DrawLevelField(newax, neway);
6500 void Life(int ax, int ay)
6504 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6507 int element = Feld[ax][ay];
6508 int graphic = el2img(element);
6509 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6511 boolean changed = FALSE;
6513 if (IS_ANIMATED(graphic))
6514 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6519 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6520 MovDelay[ax][ay] = life_time;
6522 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6525 if (MovDelay[ax][ay])
6529 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6531 int xx = ax+x1, yy = ay+y1;
6534 if (!IN_LEV_FIELD(xx, yy))
6537 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6539 int x = xx+x2, y = yy+y2;
6541 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6544 if (((Feld[x][y] == element ||
6545 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6547 (IS_FREE(x, y) && Stop[x][y]))
6551 if (xx == ax && yy == ay) /* field in the middle */
6553 if (nachbarn < life_parameter[0] ||
6554 nachbarn > life_parameter[1])
6556 Feld[xx][yy] = EL_EMPTY;
6558 DrawLevelField(xx, yy);
6559 Stop[xx][yy] = TRUE;
6563 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6564 { /* free border field */
6565 if (nachbarn >= life_parameter[2] &&
6566 nachbarn <= life_parameter[3])
6568 Feld[xx][yy] = element;
6569 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6571 DrawLevelField(xx, yy);
6572 Stop[xx][yy] = TRUE;
6579 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6580 SND_GAME_OF_LIFE_GROWING);
6583 static void InitRobotWheel(int x, int y)
6585 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6588 static void RunRobotWheel(int x, int y)
6590 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6593 static void StopRobotWheel(int x, int y)
6595 if (ZX == x && ZY == y)
6599 static void InitTimegateWheel(int x, int y)
6601 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6604 static void RunTimegateWheel(int x, int y)
6606 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6609 static void InitMagicBallDelay(int x, int y)
6611 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND;
6613 if (ChangeDelay[x][y] == 0)
6614 ChangeDelay[x][y] = 1;
6617 static void ActivateMagicBall(int bx, int by)
6621 if (level.ball_random)
6623 int pos_border = RND(8); /* select one of the eight border elements */
6624 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
6625 int xx = pos_content % 3;
6626 int yy = pos_content / 3;
6631 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6632 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6636 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
6638 int xx = x - bx + 1;
6639 int yy = y - by + 1;
6641 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6642 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6646 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
6649 void CheckExit(int x, int y)
6651 if (local_player->gems_still_needed > 0 ||
6652 local_player->sokobanfields_still_needed > 0 ||
6653 local_player->lights_still_needed > 0)
6655 int element = Feld[x][y];
6656 int graphic = el2img(element);
6658 if (IS_ANIMATED(graphic))
6659 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6664 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6667 Feld[x][y] = EL_EXIT_OPENING;
6669 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6672 void CheckExitSP(int x, int y)
6674 if (local_player->gems_still_needed > 0)
6676 int element = Feld[x][y];
6677 int graphic = el2img(element);
6679 if (IS_ANIMATED(graphic))
6680 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6685 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6688 Feld[x][y] = EL_SP_EXIT_OPENING;
6690 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6693 static void CloseAllOpenTimegates()
6698 SCAN_PLAYFIELD(x, y)
6700 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6703 int element = Feld[x][y];
6705 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6707 Feld[x][y] = EL_TIMEGATE_CLOSING;
6709 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6714 void EdelsteinFunkeln(int x, int y)
6716 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6719 if (Feld[x][y] == EL_BD_DIAMOND)
6722 if (MovDelay[x][y] == 0) /* next animation frame */
6723 MovDelay[x][y] = 11 * !SimpleRND(500);
6725 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6729 if (setup.direct_draw && MovDelay[x][y])
6730 SetDrawtoField(DRAW_BUFFERED);
6732 DrawLevelElementAnimation(x, y, Feld[x][y]);
6734 if (MovDelay[x][y] != 0)
6736 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6737 10 - MovDelay[x][y]);
6739 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6741 if (setup.direct_draw)
6745 dest_x = FX + SCREENX(x) * TILEX;
6746 dest_y = FY + SCREENY(y) * TILEY;
6748 BlitBitmap(drawto_field, window,
6749 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6750 SetDrawtoField(DRAW_DIRECT);
6756 void MauerWaechst(int x, int y)
6760 if (!MovDelay[x][y]) /* next animation frame */
6761 MovDelay[x][y] = 3 * delay;
6763 if (MovDelay[x][y]) /* wait some time before next frame */
6767 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6769 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6770 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6772 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6775 if (!MovDelay[x][y])
6777 if (MovDir[x][y] == MV_LEFT)
6779 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6780 DrawLevelField(x - 1, y);
6782 else if (MovDir[x][y] == MV_RIGHT)
6784 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6785 DrawLevelField(x + 1, y);
6787 else if (MovDir[x][y] == MV_UP)
6789 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6790 DrawLevelField(x, y - 1);
6794 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6795 DrawLevelField(x, y + 1);
6798 Feld[x][y] = Store[x][y];
6800 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6801 DrawLevelField(x, y);
6806 void MauerAbleger(int ax, int ay)
6808 int element = Feld[ax][ay];
6809 int graphic = el2img(element);
6810 boolean oben_frei = FALSE, unten_frei = FALSE;
6811 boolean links_frei = FALSE, rechts_frei = FALSE;
6812 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6813 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6814 boolean new_wall = FALSE;
6816 if (IS_ANIMATED(graphic))
6817 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6819 if (!MovDelay[ax][ay]) /* start building new wall */
6820 MovDelay[ax][ay] = 6;
6822 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6825 if (MovDelay[ax][ay])
6829 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6831 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6833 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6835 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6838 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6839 element == EL_EXPANDABLE_WALL_ANY)
6843 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6844 Store[ax][ay-1] = element;
6845 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6846 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6847 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6848 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6853 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6854 Store[ax][ay+1] = element;
6855 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6856 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6857 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6858 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6863 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6864 element == EL_EXPANDABLE_WALL_ANY ||
6865 element == EL_EXPANDABLE_WALL)
6869 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6870 Store[ax-1][ay] = element;
6871 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6872 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6873 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6874 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6880 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6881 Store[ax+1][ay] = element;
6882 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6883 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6884 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6885 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6890 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6891 DrawLevelField(ax, ay);
6893 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6895 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6896 unten_massiv = TRUE;
6897 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6898 links_massiv = TRUE;
6899 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6900 rechts_massiv = TRUE;
6902 if (((oben_massiv && unten_massiv) ||
6903 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6904 element == EL_EXPANDABLE_WALL) &&
6905 ((links_massiv && rechts_massiv) ||
6906 element == EL_EXPANDABLE_WALL_VERTICAL))
6907 Feld[ax][ay] = EL_WALL;
6910 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6913 void CheckForDragon(int x, int y)
6916 boolean dragon_found = FALSE;
6917 static int xy[4][2] =
6925 for (i = 0; i < NUM_DIRECTIONS; i++)
6927 for (j = 0; j < 4; j++)
6929 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6931 if (IN_LEV_FIELD(xx, yy) &&
6932 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6934 if (Feld[xx][yy] == EL_DRAGON)
6935 dragon_found = TRUE;
6944 for (i = 0; i < NUM_DIRECTIONS; i++)
6946 for (j = 0; j < 3; j++)
6948 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6950 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6952 Feld[xx][yy] = EL_EMPTY;
6953 DrawLevelField(xx, yy);
6962 static void InitBuggyBase(int x, int y)
6964 int element = Feld[x][y];
6965 int activating_delay = FRAMES_PER_SECOND / 4;
6968 (element == EL_SP_BUGGY_BASE ?
6969 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6970 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6972 element == EL_SP_BUGGY_BASE_ACTIVE ?
6973 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6976 static void WarnBuggyBase(int x, int y)
6979 static int xy[4][2] =
6987 for (i = 0; i < NUM_DIRECTIONS; i++)
6989 int xx = x + xy[i][0], yy = y + xy[i][1];
6991 if (IS_PLAYER(xx, yy))
6993 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7000 static void InitTrap(int x, int y)
7002 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7005 static void ActivateTrap(int x, int y)
7007 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7010 static void ChangeActiveTrap(int x, int y)
7012 int graphic = IMG_TRAP_ACTIVE;
7014 /* if new animation frame was drawn, correct crumbled sand border */
7015 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7016 DrawLevelFieldCrumbledSand(x, y);
7019 static int getSpecialActionElement(int element, int number, int base_element)
7021 return (element != EL_EMPTY ? element :
7022 number != -1 ? base_element + number - 1 :
7026 static int getModifiedActionNumber(int value_old, int operator, int operand,
7027 int value_min, int value_max)
7029 int value_new = (operator == CA_MODE_SET ? operand :
7030 operator == CA_MODE_ADD ? value_old + operand :
7031 operator == CA_MODE_SUBTRACT ? value_old - operand :
7032 operator == CA_MODE_MULTIPLY ? value_old * operand :
7033 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7034 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7037 return (value_new < value_min ? value_min :
7038 value_new > value_max ? value_max :
7042 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7044 struct ElementInfo *ei = &element_info[element];
7045 struct ElementChangeInfo *change = &ei->change_page[page];
7046 int action_type = change->action_type;
7047 int action_mode = change->action_mode;
7048 int action_arg = change->action_arg;
7051 if (!change->has_action)
7054 /* ---------- determine action paramater values -------------------------- */
7056 int level_time_value =
7057 (level.time > 0 ? TimeLeft :
7060 int action_arg_element =
7061 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7062 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7063 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7066 int action_arg_direction =
7067 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7068 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7069 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7070 change->actual_trigger_side :
7071 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7072 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7075 int action_arg_number_min =
7076 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
7079 int action_arg_number_max =
7080 (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
7081 action_type == CA_SET_LEVEL_GEMS ? 999 :
7082 action_type == CA_SET_LEVEL_TIME ? 9999 :
7083 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7084 action_type == CA_SET_CE_SCORE ? 9999 :
7085 action_type == CA_SET_CE_VALUE ? 9999 :
7088 int action_arg_number_reset =
7089 (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
7090 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7091 action_type == CA_SET_LEVEL_TIME ? level.time :
7092 action_type == CA_SET_LEVEL_SCORE ? 0 :
7093 action_type == CA_SET_CE_SCORE ? 0 :
7095 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7097 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7101 int action_arg_number =
7102 (action_arg <= CA_ARG_MAX ? action_arg :
7103 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7104 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7105 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7106 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7107 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7108 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7109 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7110 #if USE_NEW_CUSTOM_VALUE
7111 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7113 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7115 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7116 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7117 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7118 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7119 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7120 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
7123 int action_arg_number_old =
7124 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7125 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7126 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7127 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7128 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7131 int action_arg_number_new =
7132 getModifiedActionNumber(action_arg_number_old,
7133 action_mode, action_arg_number,
7134 action_arg_number_min, action_arg_number_max);
7136 int trigger_player_bits =
7137 (change->actual_trigger_player >= EL_PLAYER_1 &&
7138 change->actual_trigger_player <= EL_PLAYER_4 ?
7139 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7142 int action_arg_player_bits =
7143 (action_arg >= CA_ARG_PLAYER_1 &&
7144 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7145 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7148 /* ---------- execute action -------------------------------------------- */
7157 /* ---------- level actions ------------------------------------------- */
7159 case CA_RESTART_LEVEL:
7161 game.restart_level = TRUE;
7166 case CA_SHOW_ENVELOPE:
7168 int element = getSpecialActionElement(action_arg_element,
7169 action_arg_number, EL_ENVELOPE_1);
7171 if (IS_ENVELOPE(element))
7172 local_player->show_envelope = element;
7177 case CA_SET_LEVEL_TIME:
7179 if (level.time > 0) /* only modify limited time value */
7181 TimeLeft = action_arg_number_new;
7183 DrawGameValue_Time(TimeLeft);
7185 if (!TimeLeft && setup.time_limit)
7186 for (i = 0; i < MAX_PLAYERS; i++)
7187 KillPlayer(&stored_player[i]);
7193 case CA_SET_LEVEL_SCORE:
7195 local_player->score = action_arg_number_new;
7197 DrawGameValue_Score(local_player->score);
7202 case CA_SET_LEVEL_GEMS:
7204 local_player->gems_still_needed = action_arg_number_new;
7206 DrawGameValue_Emeralds(local_player->gems_still_needed);
7211 case CA_SET_LEVEL_GRAVITY:
7213 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7214 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7215 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7220 case CA_SET_LEVEL_WIND:
7222 game.wind_direction = action_arg_direction;
7227 /* ---------- player actions ------------------------------------------ */
7229 case CA_MOVE_PLAYER:
7231 /* automatically move to the next field in specified direction */
7232 for (i = 0; i < MAX_PLAYERS; i++)
7233 if (trigger_player_bits & (1 << i))
7234 stored_player[i].programmed_action = action_arg_direction;
7239 case CA_EXIT_PLAYER:
7241 for (i = 0; i < MAX_PLAYERS; i++)
7242 if (action_arg_player_bits & (1 << i))
7243 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7248 case CA_KILL_PLAYER:
7250 for (i = 0; i < MAX_PLAYERS; i++)
7251 if (action_arg_player_bits & (1 << i))
7252 KillPlayer(&stored_player[i]);
7257 case CA_SET_PLAYER_KEYS:
7259 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7260 int element = getSpecialActionElement(action_arg_element,
7261 action_arg_number, EL_KEY_1);
7263 if (IS_KEY(element))
7265 for (i = 0; i < MAX_PLAYERS; i++)
7267 if (trigger_player_bits & (1 << i))
7269 stored_player[i].key[KEY_NR(element)] = key_state;
7271 DrawGameValue_Keys(stored_player[i].key);
7273 redraw_mask |= REDRAW_DOOR_1;
7281 case CA_SET_PLAYER_SPEED:
7283 for (i = 0; i < MAX_PLAYERS; i++)
7285 if (trigger_player_bits & (1 << i))
7287 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7289 if (action_arg == CA_ARG_SPEED_SLOWER ||
7290 action_arg == CA_ARG_SPEED_FASTER)
7292 action_arg_number = 2;
7293 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7298 getModifiedActionNumber(move_stepsize,
7301 action_arg_number_min,
7302 action_arg_number_max);
7304 /* make sure that value is power of 2 */
7305 move_stepsize = (1 << log_2(move_stepsize));
7307 /* do no immediately change -- the player might just be moving */
7308 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7310 stored_player[i].cannot_move =
7311 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7318 case CA_SET_PLAYER_SHIELD:
7320 for (i = 0; i < MAX_PLAYERS; i++)
7322 if (trigger_player_bits & (1 << i))
7324 if (action_arg == CA_ARG_SHIELD_OFF)
7326 stored_player[i].shield_normal_time_left = 0;
7327 stored_player[i].shield_deadly_time_left = 0;
7329 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7331 stored_player[i].shield_normal_time_left = 999999;
7333 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7335 stored_player[i].shield_normal_time_left = 999999;
7336 stored_player[i].shield_deadly_time_left = 999999;
7344 case CA_SET_PLAYER_ARTWORK:
7346 for (i = 0; i < MAX_PLAYERS; i++)
7348 if (trigger_player_bits & (1 << i))
7350 int artwork_element = action_arg_element;
7352 if (action_arg == CA_ARG_ELEMENT_RESET)
7354 (level.use_artwork_element[i] ? level.artwork_element[i] :
7355 stored_player[i].element_nr);
7357 stored_player[i].artwork_element = artwork_element;
7359 SetPlayerWaiting(&stored_player[i], FALSE);
7361 /* set number of special actions for bored and sleeping animation */
7362 stored_player[i].num_special_action_bored =
7363 get_num_special_action(artwork_element,
7364 ACTION_BORING_1, ACTION_BORING_LAST);
7365 stored_player[i].num_special_action_sleeping =
7366 get_num_special_action(artwork_element,
7367 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7374 /* ---------- CE actions ---------------------------------------------- */
7376 case CA_SET_CE_SCORE:
7378 ei->collect_score = action_arg_number_new;
7383 case CA_SET_CE_VALUE:
7385 #if USE_NEW_CUSTOM_VALUE
7386 int last_custom_value = CustomValue[x][y];
7388 CustomValue[x][y] = action_arg_number_new;
7391 printf("::: Count == %d\n", CustomValue[x][y]);
7394 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7397 printf("::: CE_VALUE_GETS_ZERO\n");
7400 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7401 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7408 /* ---------- engine actions ------------------------------------------ */
7410 case CA_SET_ENGINE_SCAN_MODE:
7412 InitPlayfieldScanMode(action_arg);
7422 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7424 int previous_move_direction = MovDir[x][y];
7425 #if USE_NEW_CUSTOM_VALUE
7426 int last_ce_value = CustomValue[x][y];
7428 boolean add_player = (ELEM_IS_PLAYER(element) &&
7429 IS_WALKABLE(Feld[x][y]));
7431 /* check if element under player changes from accessible to unaccessible
7432 (needed for special case of dropping element which then changes) */
7433 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7434 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7443 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7444 RemoveMovingField(x, y);
7448 Feld[x][y] = element;
7450 ResetGfxAnimation(x, y);
7451 ResetRandomAnimationValue(x, y);
7453 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7454 MovDir[x][y] = previous_move_direction;
7456 #if USE_NEW_CUSTOM_VALUE
7457 if (element_info[Feld[x][y]].use_last_ce_value)
7458 CustomValue[x][y] = last_ce_value;
7461 InitField_WithBug1(x, y, FALSE);
7463 DrawLevelField(x, y);
7465 if (GFX_CRUMBLED(Feld[x][y]))
7466 DrawLevelFieldCrumbledSandNeighbours(x, y);
7469 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7470 if (ELEM_IS_PLAYER(element))
7471 RelocatePlayer(x, y, element);
7474 ChangeCount[x][y]++; /* count number of changes in the same frame */
7476 TestIfBadThingTouchesPlayer(x, y);
7477 TestIfPlayerTouchesCustomElement(x, y);
7478 TestIfElementTouchesCustomElement(x, y);
7481 static void CreateField(int x, int y, int element)
7483 CreateFieldExt(x, y, element, FALSE);
7486 static void CreateElementFromChange(int x, int y, int element)
7488 CreateFieldExt(x, y, element, TRUE);
7491 static boolean ChangeElement(int x, int y, int element, int page)
7493 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7495 int old_element = Feld[x][y];
7497 /* always use default change event to prevent running into a loop */
7498 if (ChangeEvent[x][y] == -1)
7499 ChangeEvent[x][y] = CE_DELAY;
7501 if (ChangeEvent[x][y] == CE_DELAY)
7503 /* reset actual trigger element, trigger player and action element */
7504 change->actual_trigger_element = EL_EMPTY;
7505 change->actual_trigger_player = EL_PLAYER_1;
7506 change->actual_trigger_side = CH_SIDE_NONE;
7507 change->actual_trigger_ce_value = 0;
7510 /* do not change elements more than a specified maximum number of changes */
7511 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7514 ChangeCount[x][y]++; /* count number of changes in the same frame */
7516 if (change->explode)
7523 if (change->use_target_content)
7525 boolean complete_replace = TRUE;
7526 boolean can_replace[3][3];
7529 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7532 boolean is_walkable;
7533 boolean is_diggable;
7534 boolean is_collectible;
7535 boolean is_removable;
7536 boolean is_destructible;
7537 int ex = x + xx - 1;
7538 int ey = y + yy - 1;
7539 int content_element = change->target_content.e[xx][yy];
7542 can_replace[xx][yy] = TRUE;
7544 if (ex == x && ey == y) /* do not check changing element itself */
7547 if (content_element == EL_EMPTY_SPACE)
7549 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7554 if (!IN_LEV_FIELD(ex, ey))
7556 can_replace[xx][yy] = FALSE;
7557 complete_replace = FALSE;
7564 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7565 e = MovingOrBlocked2Element(ex, ey);
7567 is_empty = (IS_FREE(ex, ey) ||
7568 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7570 is_walkable = (is_empty || IS_WALKABLE(e));
7571 is_diggable = (is_empty || IS_DIGGABLE(e));
7572 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7573 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7574 is_removable = (is_diggable || is_collectible);
7576 can_replace[xx][yy] =
7577 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7578 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7579 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7580 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7581 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7582 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7583 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7585 if (!can_replace[xx][yy])
7586 complete_replace = FALSE;
7589 if (!change->only_if_complete || complete_replace)
7591 boolean something_has_changed = FALSE;
7593 if (change->only_if_complete && change->use_random_replace &&
7594 RND(100) < change->random_percentage)
7597 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7599 int ex = x + xx - 1;
7600 int ey = y + yy - 1;
7601 int content_element;
7603 if (can_replace[xx][yy] && (!change->use_random_replace ||
7604 RND(100) < change->random_percentage))
7606 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7607 RemoveMovingField(ex, ey);
7609 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7611 content_element = change->target_content.e[xx][yy];
7612 target_element = GET_TARGET_ELEMENT(content_element, change);
7614 CreateElementFromChange(ex, ey, target_element);
7616 something_has_changed = TRUE;
7618 /* for symmetry reasons, freeze newly created border elements */
7619 if (ex != x || ey != y)
7620 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7624 if (something_has_changed)
7626 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7627 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7633 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7635 CreateElementFromChange(x, y, target_element);
7637 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7638 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7641 /* this uses direct change before indirect change */
7642 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7647 #if USE_NEW_DELAYED_ACTION
7649 static void HandleElementChange(int x, int y, int page)
7651 int element = MovingOrBlocked2Element(x, y);
7652 struct ElementInfo *ei = &element_info[element];
7653 struct ElementChangeInfo *change = &ei->change_page[page];
7656 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7657 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7660 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7661 x, y, element, element_info[element].token_name);
7662 printf("HandleElementChange(): This should never happen!\n");
7667 /* this can happen with classic bombs on walkable, changing elements */
7668 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7671 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7672 ChangeDelay[x][y] = 0;
7678 if (ChangeDelay[x][y] == 0) /* initialize element change */
7680 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7682 if (change->can_change)
7684 ResetGfxAnimation(x, y);
7685 ResetRandomAnimationValue(x, y);
7687 if (change->pre_change_function)
7688 change->pre_change_function(x, y);
7692 ChangeDelay[x][y]--;
7694 if (ChangeDelay[x][y] != 0) /* continue element change */
7696 if (change->can_change)
7698 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7700 if (IS_ANIMATED(graphic))
7701 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7703 if (change->change_function)
7704 change->change_function(x, y);
7707 else /* finish element change */
7709 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7711 page = ChangePage[x][y];
7712 ChangePage[x][y] = -1;
7714 change = &ei->change_page[page];
7717 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7719 ChangeDelay[x][y] = 1; /* try change after next move step */
7720 ChangePage[x][y] = page; /* remember page to use for change */
7725 if (change->can_change)
7727 if (ChangeElement(x, y, element, page))
7729 if (change->post_change_function)
7730 change->post_change_function(x, y);
7734 if (change->has_action)
7735 ExecuteCustomElementAction(x, y, element, page);
7741 static void HandleElementChange(int x, int y, int page)
7743 int element = MovingOrBlocked2Element(x, y);
7744 struct ElementInfo *ei = &element_info[element];
7745 struct ElementChangeInfo *change = &ei->change_page[page];
7748 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7751 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7752 x, y, element, element_info[element].token_name);
7753 printf("HandleElementChange(): This should never happen!\n");
7758 /* this can happen with classic bombs on walkable, changing elements */
7759 if (!CAN_CHANGE(element))
7762 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7763 ChangeDelay[x][y] = 0;
7769 if (ChangeDelay[x][y] == 0) /* initialize element change */
7771 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7773 ResetGfxAnimation(x, y);
7774 ResetRandomAnimationValue(x, y);
7776 if (change->pre_change_function)
7777 change->pre_change_function(x, y);
7780 ChangeDelay[x][y]--;
7782 if (ChangeDelay[x][y] != 0) /* continue element change */
7784 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7786 if (IS_ANIMATED(graphic))
7787 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7789 if (change->change_function)
7790 change->change_function(x, y);
7792 else /* finish element change */
7794 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7796 page = ChangePage[x][y];
7797 ChangePage[x][y] = -1;
7799 change = &ei->change_page[page];
7802 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7804 ChangeDelay[x][y] = 1; /* try change after next move step */
7805 ChangePage[x][y] = page; /* remember page to use for change */
7810 if (ChangeElement(x, y, element, page))
7812 if (change->post_change_function)
7813 change->post_change_function(x, y);
7820 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
7821 int trigger_element,
7827 boolean change_done_any = FALSE;
7828 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7831 if (!(trigger_events[trigger_element][trigger_event]))
7834 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7836 int element = EL_CUSTOM_START + i;
7837 boolean change_done = FALSE;
7840 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7841 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7844 for (p = 0; p < element_info[element].num_change_pages; p++)
7846 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7848 if (change->can_change_or_has_action &&
7849 change->has_event[trigger_event] &&
7850 change->trigger_side & trigger_side &&
7851 change->trigger_player & trigger_player &&
7852 change->trigger_page & trigger_page_bits &&
7853 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7855 change->actual_trigger_element = trigger_element;
7856 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7857 change->actual_trigger_side = trigger_side;
7858 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
7860 if ((change->can_change && !change_done) || change->has_action)
7865 SCAN_PLAYFIELD(x, y)
7867 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7870 if (Feld[x][y] == element)
7872 if (change->can_change && !change_done)
7874 ChangeDelay[x][y] = 1;
7875 ChangeEvent[x][y] = trigger_event;
7877 HandleElementChange(x, y, p);
7879 #if USE_NEW_DELAYED_ACTION
7880 else if (change->has_action)
7882 ExecuteCustomElementAction(x, y, element, p);
7883 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7886 if (change->has_action)
7888 ExecuteCustomElementAction(x, y, element, p);
7889 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7895 if (change->can_change)
7898 change_done_any = TRUE;
7905 return change_done_any;
7908 static boolean CheckElementChangeExt(int x, int y,
7910 int trigger_element,
7915 boolean change_done = FALSE;
7918 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7919 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7922 if (Feld[x][y] == EL_BLOCKED)
7924 Blocked2Moving(x, y, &x, &y);
7925 element = Feld[x][y];
7928 if (Feld[x][y] != element) /* check if element has already changed */
7931 for (p = 0; p < element_info[element].num_change_pages; p++)
7933 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7935 boolean check_trigger_element =
7936 (trigger_event == CE_TOUCHING_X ||
7937 trigger_event == CE_HITTING_X ||
7938 trigger_event == CE_HIT_BY_X);
7940 if (change->can_change_or_has_action &&
7941 change->has_event[trigger_event] &&
7942 change->trigger_side & trigger_side &&
7943 change->trigger_player & trigger_player &&
7944 (!check_trigger_element ||
7945 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7947 change->actual_trigger_element = trigger_element;
7948 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7949 change->actual_trigger_side = trigger_side;
7950 change->actual_trigger_ce_value = CustomValue[x][y];
7952 /* special case: trigger element not at (x,y) position for some events */
7953 if (check_trigger_element)
7965 { 0, 0 }, { 0, 0 }, { 0, 0 },
7969 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
7970 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
7972 change->actual_trigger_ce_value = CustomValue[xx][yy];
7975 if (change->can_change && !change_done)
7977 ChangeDelay[x][y] = 1;
7978 ChangeEvent[x][y] = trigger_event;
7980 HandleElementChange(x, y, p);
7984 #if USE_NEW_DELAYED_ACTION
7985 else if (change->has_action)
7987 ExecuteCustomElementAction(x, y, element, p);
7988 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7991 if (change->has_action)
7993 ExecuteCustomElementAction(x, y, element, p);
7994 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8003 static void PlayPlayerSound(struct PlayerInfo *player)
8005 int jx = player->jx, jy = player->jy;
8006 int sound_element = player->artwork_element;
8007 int last_action = player->last_action_waiting;
8008 int action = player->action_waiting;
8010 if (player->is_waiting)
8012 if (action != last_action)
8013 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8015 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8019 if (action != last_action)
8020 StopSound(element_info[sound_element].sound[last_action]);
8022 if (last_action == ACTION_SLEEPING)
8023 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8027 static void PlayAllPlayersSound()
8031 for (i = 0; i < MAX_PLAYERS; i++)
8032 if (stored_player[i].active)
8033 PlayPlayerSound(&stored_player[i]);
8036 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8038 boolean last_waiting = player->is_waiting;
8039 int move_dir = player->MovDir;
8041 player->last_action_waiting = player->action_waiting;
8045 if (!last_waiting) /* not waiting -> waiting */
8047 player->is_waiting = TRUE;
8049 player->frame_counter_bored =
8051 game.player_boring_delay_fixed +
8052 SimpleRND(game.player_boring_delay_random);
8053 player->frame_counter_sleeping =
8055 game.player_sleeping_delay_fixed +
8056 SimpleRND(game.player_sleeping_delay_random);
8058 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8061 if (game.player_sleeping_delay_fixed +
8062 game.player_sleeping_delay_random > 0 &&
8063 player->anim_delay_counter == 0 &&
8064 player->post_delay_counter == 0 &&
8065 FrameCounter >= player->frame_counter_sleeping)
8066 player->is_sleeping = TRUE;
8067 else if (game.player_boring_delay_fixed +
8068 game.player_boring_delay_random > 0 &&
8069 FrameCounter >= player->frame_counter_bored)
8070 player->is_bored = TRUE;
8072 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8073 player->is_bored ? ACTION_BORING :
8076 if (player->is_sleeping)
8078 if (player->num_special_action_sleeping > 0)
8080 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8082 int last_special_action = player->special_action_sleeping;
8083 int num_special_action = player->num_special_action_sleeping;
8084 int special_action =
8085 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8086 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8087 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8088 last_special_action + 1 : ACTION_SLEEPING);
8089 int special_graphic =
8090 el_act_dir2img(player->artwork_element, special_action, move_dir);
8092 player->anim_delay_counter =
8093 graphic_info[special_graphic].anim_delay_fixed +
8094 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8095 player->post_delay_counter =
8096 graphic_info[special_graphic].post_delay_fixed +
8097 SimpleRND(graphic_info[special_graphic].post_delay_random);
8099 player->special_action_sleeping = special_action;
8102 if (player->anim_delay_counter > 0)
8104 player->action_waiting = player->special_action_sleeping;
8105 player->anim_delay_counter--;
8107 else if (player->post_delay_counter > 0)
8109 player->post_delay_counter--;
8113 else if (player->is_bored)
8115 if (player->num_special_action_bored > 0)
8117 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8119 int special_action =
8120 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8121 int special_graphic =
8122 el_act_dir2img(player->artwork_element, special_action, move_dir);
8124 player->anim_delay_counter =
8125 graphic_info[special_graphic].anim_delay_fixed +
8126 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8127 player->post_delay_counter =
8128 graphic_info[special_graphic].post_delay_fixed +
8129 SimpleRND(graphic_info[special_graphic].post_delay_random);
8131 player->special_action_bored = special_action;
8134 if (player->anim_delay_counter > 0)
8136 player->action_waiting = player->special_action_bored;
8137 player->anim_delay_counter--;
8139 else if (player->post_delay_counter > 0)
8141 player->post_delay_counter--;
8146 else if (last_waiting) /* waiting -> not waiting */
8148 player->is_waiting = FALSE;
8149 player->is_bored = FALSE;
8150 player->is_sleeping = FALSE;
8152 player->frame_counter_bored = -1;
8153 player->frame_counter_sleeping = -1;
8155 player->anim_delay_counter = 0;
8156 player->post_delay_counter = 0;
8158 player->action_waiting = ACTION_DEFAULT;
8160 player->special_action_bored = ACTION_DEFAULT;
8161 player->special_action_sleeping = ACTION_DEFAULT;
8165 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8167 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8168 int left = player_action & JOY_LEFT;
8169 int right = player_action & JOY_RIGHT;
8170 int up = player_action & JOY_UP;
8171 int down = player_action & JOY_DOWN;
8172 int button1 = player_action & JOY_BUTTON_1;
8173 int button2 = player_action & JOY_BUTTON_2;
8174 int dx = (left ? -1 : right ? 1 : 0);
8175 int dy = (up ? -1 : down ? 1 : 0);
8177 if (!player->active || tape.pausing)
8183 snapped = SnapField(player, dx, dy);
8187 dropped = DropElement(player);
8189 moved = MovePlayer(player, dx, dy);
8192 if (tape.single_step && tape.recording && !tape.pausing)
8194 if (button1 || (dropped && !moved))
8196 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8197 SnapField(player, 0, 0); /* stop snapping */
8201 SetPlayerWaiting(player, FALSE);
8203 return player_action;
8207 /* no actions for this player (no input at player's configured device) */
8209 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8210 SnapField(player, 0, 0);
8211 CheckGravityMovementWhenNotMoving(player);
8213 if (player->MovPos == 0)
8214 SetPlayerWaiting(player, TRUE);
8216 if (player->MovPos == 0) /* needed for tape.playing */
8217 player->is_moving = FALSE;
8219 player->is_dropping = FALSE;
8225 void AdvanceFrameAndPlayerCounters(int player_nr)
8229 /* advance frame counters (global frame counter and time frame counter) */
8233 /* advance player counters (counters for move delay, move animation etc.) */
8234 for (i = 0; i < MAX_PLAYERS; i++)
8236 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8237 int move_delay_value = stored_player[i].move_delay_value;
8238 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8240 if (!advance_player_counters) /* not all players may be affected */
8243 #if USE_NEW_PLAYER_ANIM
8244 if (move_frames == 0) /* less than one move per game frame */
8246 int stepsize = TILEX / move_delay_value;
8247 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8248 int count = (stored_player[i].is_moving ?
8249 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8251 if (count % delay == 0)
8256 stored_player[i].Frame += move_frames;
8258 if (stored_player[i].MovPos != 0)
8259 stored_player[i].StepFrame += move_frames;
8261 if (stored_player[i].move_delay > 0)
8262 stored_player[i].move_delay--;
8264 /* due to bugs in previous versions, counter must count up, not down */
8265 if (stored_player[i].push_delay != -1)
8266 stored_player[i].push_delay++;
8268 if (stored_player[i].drop_delay > 0)
8269 stored_player[i].drop_delay--;
8275 static unsigned long game_frame_delay = 0;
8276 unsigned long game_frame_delay_value;
8277 int magic_wall_x = 0, magic_wall_y = 0;
8278 int i, x, y, element, graphic;
8279 byte *recorded_player_action;
8280 byte summarized_player_action = 0;
8281 byte tape_action[MAX_PLAYERS];
8283 if (game_status != GAME_MODE_PLAYING)
8286 game_frame_delay_value =
8287 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8289 if (tape.playing && tape.warp_forward && !tape.pausing)
8290 game_frame_delay_value = 0;
8292 /* ---------- main game synchronization point ---------- */
8294 InitPlayfieldScanModeVars();
8296 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8298 if (network_playing && !network_player_action_received)
8300 /* try to get network player actions in time */
8302 #if defined(NETWORK_AVALIABLE)
8303 /* last chance to get network player actions without main loop delay */
8307 /* game was quit by network peer */
8308 if (game_status != GAME_MODE_PLAYING)
8311 if (!network_player_action_received)
8312 return; /* failed to get network player actions in time */
8318 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8321 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8322 if (recorded_player_action == NULL && tape.pausing)
8326 for (i = 0; i < MAX_PLAYERS; i++)
8328 summarized_player_action |= stored_player[i].action;
8330 if (!network_playing)
8331 stored_player[i].effective_action = stored_player[i].action;
8334 #if defined(NETWORK_AVALIABLE)
8335 if (network_playing)
8336 SendToServer_MovePlayer(summarized_player_action);
8339 if (!options.network && !setup.team_mode)
8340 local_player->effective_action = summarized_player_action;
8342 if (recorded_player_action != NULL)
8343 for (i = 0; i < MAX_PLAYERS; i++)
8344 stored_player[i].effective_action = recorded_player_action[i];
8346 for (i = 0; i < MAX_PLAYERS; i++)
8348 tape_action[i] = stored_player[i].effective_action;
8350 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8351 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8354 /* only save actions from input devices, but not programmed actions */
8356 TapeRecordAction(tape_action);
8358 for (i = 0; i < MAX_PLAYERS; i++)
8360 int actual_player_action = stored_player[i].effective_action;
8363 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8364 - rnd_equinox_tetrachloride 048
8365 - rnd_equinox_tetrachloride_ii 096
8366 - rnd_emanuel_schmieg 002
8367 - doctor_sloan_ww 001, 020
8369 if (stored_player[i].MovPos == 0)
8370 CheckGravityMovement(&stored_player[i]);
8373 /* overwrite programmed action with tape action */
8374 if (stored_player[i].programmed_action)
8375 actual_player_action = stored_player[i].programmed_action;
8378 PlayerActions(&stored_player[i], actual_player_action);
8380 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8382 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8383 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8386 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8389 network_player_action_received = FALSE;
8391 ScrollScreen(NULL, SCROLL_GO_ON);
8393 /* for backwards compatibility, the following code emulates a fixed bug that
8394 occured when pushing elements (causing elements that just made their last
8395 pushing step to already (if possible) make their first falling step in the
8396 same game frame, which is bad); this code is also needed to use the famous
8397 "spring push bug" which is used in older levels and might be wanted to be
8398 used also in newer levels, but in this case the buggy pushing code is only
8399 affecting the "spring" element and no other elements */
8401 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8403 for (i = 0; i < MAX_PLAYERS; i++)
8405 struct PlayerInfo *player = &stored_player[i];
8409 if (player->active && player->is_pushing && player->is_moving &&
8411 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8412 Feld[x][y] == EL_SPRING))
8414 ContinueMoving(x, y);
8416 /* continue moving after pushing (this is actually a bug) */
8417 if (!IS_MOVING(x, y))
8426 SCAN_PLAYFIELD(x, y)
8428 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8431 ChangeCount[x][y] = 0;
8432 ChangeEvent[x][y] = -1;
8434 /* this must be handled before main playfield loop */
8435 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8438 if (MovDelay[x][y] <= 0)
8442 #if USE_NEW_SNAP_DELAY
8443 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8446 if (MovDelay[x][y] <= 0)
8449 DrawLevelField(x, y);
8451 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8457 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8459 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8460 printf("GameActions(): This should never happen!\n");
8462 ChangePage[x][y] = -1;
8467 if (WasJustMoving[x][y] > 0)
8468 WasJustMoving[x][y]--;
8469 if (WasJustFalling[x][y] > 0)
8470 WasJustFalling[x][y]--;
8471 if (CheckCollision[x][y] > 0)
8472 CheckCollision[x][y]--;
8476 /* reset finished pushing action (not done in ContinueMoving() to allow
8477 continous pushing animation for elements with zero push delay) */
8478 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8480 ResetGfxAnimation(x, y);
8481 DrawLevelField(x, y);
8485 if (IS_BLOCKED(x, y))
8489 Blocked2Moving(x, y, &oldx, &oldy);
8490 if (!IS_MOVING(oldx, oldy))
8492 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8493 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8494 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8495 printf("GameActions(): This should never happen!\n");
8502 SCAN_PLAYFIELD(x, y)
8504 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8507 element = Feld[x][y];
8508 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8511 printf("::: %d,%d\n", x, y);
8513 if (element == EL_ROCK)
8514 printf("::: Yo man! Rocks can fall!\n");
8517 if (graphic_info[graphic].anim_global_sync)
8518 GfxFrame[x][y] = FrameCounter;
8519 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8521 int old_gfx_frame = GfxFrame[x][y];
8523 GfxFrame[x][y] = CustomValue[x][y];
8526 if (GfxFrame[x][y] != old_gfx_frame)
8528 DrawLevelGraphicAnimation(x, y, graphic);
8530 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8532 int old_gfx_frame = GfxFrame[x][y];
8534 GfxFrame[x][y] = element_info[element].collect_score;
8537 if (GfxFrame[x][y] != old_gfx_frame)
8539 DrawLevelGraphicAnimation(x, y, graphic);
8542 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8543 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8544 ResetRandomAnimationValue(x, y);
8546 SetRandomAnimationValue(x, y);
8548 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8550 if (IS_INACTIVE(element))
8552 if (IS_ANIMATED(graphic))
8553 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8558 /* this may take place after moving, so 'element' may have changed */
8559 if (IS_CHANGING(x, y) &&
8560 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8562 int page = element_info[element].event_page_nr[CE_DELAY];
8564 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8568 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8572 if (element == EL_CUSTOM_255)
8573 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8577 HandleElementChange(x, y, page);
8579 if (CAN_CHANGE(element))
8580 HandleElementChange(x, y, page);
8582 if (HAS_ACTION(element))
8583 ExecuteCustomElementAction(x, y, element, page);
8588 element = Feld[x][y];
8589 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8592 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8596 element = Feld[x][y];
8597 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8599 if (IS_ANIMATED(graphic) &&
8602 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8604 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8605 EdelsteinFunkeln(x, y);
8607 else if ((element == EL_ACID ||
8608 element == EL_EXIT_OPEN ||
8609 element == EL_SP_EXIT_OPEN ||
8610 element == EL_SP_TERMINAL ||
8611 element == EL_SP_TERMINAL_ACTIVE ||
8612 element == EL_EXTRA_TIME ||
8613 element == EL_SHIELD_NORMAL ||
8614 element == EL_SHIELD_DEADLY) &&
8615 IS_ANIMATED(graphic))
8616 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8617 else if (IS_MOVING(x, y))
8618 ContinueMoving(x, y);
8619 else if (IS_ACTIVE_BOMB(element))
8620 CheckDynamite(x, y);
8621 else if (element == EL_AMOEBA_GROWING)
8622 AmoebeWaechst(x, y);
8623 else if (element == EL_AMOEBA_SHRINKING)
8624 AmoebaDisappearing(x, y);
8626 #if !USE_NEW_AMOEBA_CODE
8627 else if (IS_AMOEBALIVE(element))
8628 AmoebeAbleger(x, y);
8631 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8633 else if (element == EL_EXIT_CLOSED)
8635 else if (element == EL_SP_EXIT_CLOSED)
8637 else if (element == EL_EXPANDABLE_WALL_GROWING)
8639 else if (element == EL_EXPANDABLE_WALL ||
8640 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8641 element == EL_EXPANDABLE_WALL_VERTICAL ||
8642 element == EL_EXPANDABLE_WALL_ANY)
8644 else if (element == EL_FLAMES)
8645 CheckForDragon(x, y);
8646 else if (element == EL_EXPLOSION)
8647 ; /* drawing of correct explosion animation is handled separately */
8648 else if (element == EL_ELEMENT_SNAPPING)
8651 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8653 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8656 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8657 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8660 if (element == EL_CUSTOM_255 ||
8661 element == EL_CUSTOM_256)
8662 DrawLevelGraphicAnimation(x, y, graphic);
8665 if (IS_BELT_ACTIVE(element))
8666 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8668 if (game.magic_wall_active)
8670 int jx = local_player->jx, jy = local_player->jy;
8672 /* play the element sound at the position nearest to the player */
8673 if ((element == EL_MAGIC_WALL_FULL ||
8674 element == EL_MAGIC_WALL_ACTIVE ||
8675 element == EL_MAGIC_WALL_EMPTYING ||
8676 element == EL_BD_MAGIC_WALL_FULL ||
8677 element == EL_BD_MAGIC_WALL_ACTIVE ||
8678 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8679 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8687 #if USE_NEW_AMOEBA_CODE
8688 /* new experimental amoeba growth stuff */
8689 if (!(FrameCounter % 8))
8691 static unsigned long random = 1684108901;
8693 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8695 x = RND(lev_fieldx);
8696 y = RND(lev_fieldy);
8697 element = Feld[x][y];
8699 if (!IS_PLAYER(x,y) &&
8700 (element == EL_EMPTY ||
8701 CAN_GROW_INTO(element) ||
8702 element == EL_QUICKSAND_EMPTY ||
8703 element == EL_ACID_SPLASH_LEFT ||
8704 element == EL_ACID_SPLASH_RIGHT))
8706 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8707 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8708 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8709 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8710 Feld[x][y] = EL_AMOEBA_DROP;
8713 random = random * 129 + 1;
8719 if (game.explosions_delayed)
8722 game.explosions_delayed = FALSE;
8725 SCAN_PLAYFIELD(x, y)
8727 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8730 element = Feld[x][y];
8732 if (ExplodeField[x][y])
8733 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8734 else if (element == EL_EXPLOSION)
8735 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8737 ExplodeField[x][y] = EX_TYPE_NONE;
8740 game.explosions_delayed = TRUE;
8743 if (game.magic_wall_active)
8745 if (!(game.magic_wall_time_left % 4))
8747 int element = Feld[magic_wall_x][magic_wall_y];
8749 if (element == EL_BD_MAGIC_WALL_FULL ||
8750 element == EL_BD_MAGIC_WALL_ACTIVE ||
8751 element == EL_BD_MAGIC_WALL_EMPTYING)
8752 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8754 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8757 if (game.magic_wall_time_left > 0)
8759 game.magic_wall_time_left--;
8760 if (!game.magic_wall_time_left)
8763 SCAN_PLAYFIELD(x, y)
8765 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8768 element = Feld[x][y];
8770 if (element == EL_MAGIC_WALL_ACTIVE ||
8771 element == EL_MAGIC_WALL_FULL)
8773 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8774 DrawLevelField(x, y);
8776 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8777 element == EL_BD_MAGIC_WALL_FULL)
8779 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8780 DrawLevelField(x, y);
8784 game.magic_wall_active = FALSE;
8789 if (game.light_time_left > 0)
8791 game.light_time_left--;
8793 if (game.light_time_left == 0)
8794 RedrawAllLightSwitchesAndInvisibleElements();
8797 if (game.timegate_time_left > 0)
8799 game.timegate_time_left--;
8801 if (game.timegate_time_left == 0)
8802 CloseAllOpenTimegates();
8805 if (game.lenses_time_left > 0)
8807 game.lenses_time_left--;
8809 if (game.lenses_time_left == 0)
8810 RedrawAllInvisibleElementsForLenses();
8813 if (game.magnify_time_left > 0)
8815 game.magnify_time_left--;
8817 if (game.magnify_time_left == 0)
8818 RedrawAllInvisibleElementsForMagnifier();
8821 for (i = 0; i < MAX_PLAYERS; i++)
8823 struct PlayerInfo *player = &stored_player[i];
8825 if (SHIELD_ON(player))
8827 if (player->shield_deadly_time_left)
8828 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8829 else if (player->shield_normal_time_left)
8830 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8834 if (TimeFrames >= FRAMES_PER_SECOND)
8839 for (i = 0; i < MAX_PLAYERS; i++)
8841 struct PlayerInfo *player = &stored_player[i];
8843 if (SHIELD_ON(player))
8845 player->shield_normal_time_left--;
8847 if (player->shield_deadly_time_left > 0)
8848 player->shield_deadly_time_left--;
8852 if (!level.use_step_counter)
8860 if (TimeLeft <= 10 && setup.time_limit)
8861 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8863 DrawGameValue_Time(TimeLeft);
8865 if (!TimeLeft && setup.time_limit)
8866 for (i = 0; i < MAX_PLAYERS; i++)
8867 KillPlayer(&stored_player[i]);
8869 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8870 DrawGameValue_Time(TimePlayed);
8873 if (tape.recording || tape.playing)
8874 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8878 PlayAllPlayersSound();
8880 if (options.debug) /* calculate frames per second */
8882 static unsigned long fps_counter = 0;
8883 static int fps_frames = 0;
8884 unsigned long fps_delay_ms = Counter() - fps_counter;
8888 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8890 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8893 fps_counter = Counter();
8896 redraw_mask |= REDRAW_FPS;
8899 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8901 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8903 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8905 local_player->show_envelope = 0;
8908 /* use random number generator in every frame to make it less predictable */
8909 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8913 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8915 int min_x = x, min_y = y, max_x = x, max_y = y;
8918 for (i = 0; i < MAX_PLAYERS; i++)
8920 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8922 if (!stored_player[i].active || &stored_player[i] == player)
8925 min_x = MIN(min_x, jx);
8926 min_y = MIN(min_y, jy);
8927 max_x = MAX(max_x, jx);
8928 max_y = MAX(max_y, jy);
8931 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8934 static boolean AllPlayersInVisibleScreen()
8938 for (i = 0; i < MAX_PLAYERS; i++)
8940 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8942 if (!stored_player[i].active)
8945 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8952 void ScrollLevel(int dx, int dy)
8954 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8957 BlitBitmap(drawto_field, drawto_field,
8958 FX + TILEX * (dx == -1) - softscroll_offset,
8959 FY + TILEY * (dy == -1) - softscroll_offset,
8960 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8961 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8962 FX + TILEX * (dx == 1) - softscroll_offset,
8963 FY + TILEY * (dy == 1) - softscroll_offset);
8967 x = (dx == 1 ? BX1 : BX2);
8968 for (y = BY1; y <= BY2; y++)
8969 DrawScreenField(x, y);
8974 y = (dy == 1 ? BY1 : BY2);
8975 for (x = BX1; x <= BX2; x++)
8976 DrawScreenField(x, y);
8979 redraw_mask |= REDRAW_FIELD;
8982 static boolean canFallDown(struct PlayerInfo *player)
8984 int jx = player->jx, jy = player->jy;
8986 return (IN_LEV_FIELD(jx, jy + 1) &&
8987 (IS_FREE(jx, jy + 1) ||
8988 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8989 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8990 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8993 static boolean canPassField(int x, int y, int move_dir)
8995 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8996 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8997 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9000 int element = Feld[x][y];
9002 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9003 !CAN_MOVE(element) &&
9004 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9005 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9006 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9009 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9011 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9012 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9013 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9017 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9018 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9019 (IS_DIGGABLE(Feld[newx][newy]) ||
9020 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9021 canPassField(newx, newy, move_dir)));
9024 static void CheckGravityMovement(struct PlayerInfo *player)
9026 if (game.gravity && !player->programmed_action)
9028 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9029 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9030 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9031 int jx = player->jx, jy = player->jy;
9032 boolean player_is_moving_to_valid_field =
9033 (!player_is_snapping &&
9034 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9035 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9036 boolean player_can_fall_down = canFallDown(player);
9038 if (player_can_fall_down &&
9039 !player_is_moving_to_valid_field)
9040 player->programmed_action = MV_DOWN;
9044 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9046 return CheckGravityMovement(player);
9048 if (game.gravity && !player->programmed_action)
9050 int jx = player->jx, jy = player->jy;
9051 boolean field_under_player_is_free =
9052 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9053 boolean player_is_standing_on_valid_field =
9054 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9055 (IS_WALKABLE(Feld[jx][jy]) &&
9056 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9058 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9059 player->programmed_action = MV_DOWN;
9065 -----------------------------------------------------------------------------
9066 dx, dy: direction (non-diagonal) to try to move the player to
9067 real_dx, real_dy: direction as read from input device (can be diagonal)
9070 boolean MovePlayerOneStep(struct PlayerInfo *player,
9071 int dx, int dy, int real_dx, int real_dy)
9073 int jx = player->jx, jy = player->jy;
9074 int new_jx = jx + dx, new_jy = jy + dy;
9078 if (!player->active || (!dx && !dy))
9079 return MF_NO_ACTION;
9081 player->MovDir = (dx < 0 ? MV_LEFT :
9084 dy > 0 ? MV_DOWN : MV_NONE);
9086 if (!IN_LEV_FIELD(new_jx, new_jy))
9087 return MF_NO_ACTION;
9089 if (player->cannot_move)
9092 if (player->MovPos == 0)
9094 player->is_moving = FALSE;
9095 player->is_digging = FALSE;
9096 player->is_collecting = FALSE;
9097 player->is_snapping = FALSE;
9098 player->is_pushing = FALSE;
9101 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9102 SnapField(player, 0, 0);
9105 return MF_NO_ACTION;
9108 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9109 return MF_NO_ACTION;
9111 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9113 if (DONT_RUN_INTO(element))
9115 if (element == EL_ACID && dx == 0 && dy == 1)
9117 SplashAcid(new_jx, new_jy);
9118 Feld[jx][jy] = EL_PLAYER_1;
9119 InitMovingField(jx, jy, MV_DOWN);
9120 Store[jx][jy] = EL_ACID;
9121 ContinueMoving(jx, jy);
9125 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9130 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9131 if (can_move != MF_MOVING)
9134 /* check if DigField() has caused relocation of the player */
9135 if (player->jx != jx || player->jy != jy)
9136 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9138 StorePlayer[jx][jy] = 0;
9139 player->last_jx = jx;
9140 player->last_jy = jy;
9141 player->jx = new_jx;
9142 player->jy = new_jy;
9143 StorePlayer[new_jx][new_jy] = player->element_nr;
9145 if (player->move_delay_value_next != -1)
9147 player->move_delay_value = player->move_delay_value_next;
9148 player->move_delay_value_next = -1;
9152 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9154 player->step_counter++;
9156 PlayerVisit[jx][jy] = FrameCounter;
9158 ScrollPlayer(player, SCROLL_INIT);
9163 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9165 int jx = player->jx, jy = player->jy;
9166 int old_jx = jx, old_jy = jy;
9167 int moved = MF_NO_ACTION;
9169 if (!player->active)
9174 if (player->MovPos == 0)
9176 player->is_moving = FALSE;
9177 player->is_digging = FALSE;
9178 player->is_collecting = FALSE;
9179 player->is_snapping = FALSE;
9180 player->is_pushing = FALSE;
9186 if (player->move_delay > 0)
9189 player->move_delay = -1; /* set to "uninitialized" value */
9191 /* store if player is automatically moved to next field */
9192 player->is_auto_moving = (player->programmed_action != MV_NONE);
9194 /* remove the last programmed player action */
9195 player->programmed_action = 0;
9199 /* should only happen if pre-1.2 tape recordings are played */
9200 /* this is only for backward compatibility */
9202 int original_move_delay_value = player->move_delay_value;
9205 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9209 /* scroll remaining steps with finest movement resolution */
9210 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9212 while (player->MovPos)
9214 ScrollPlayer(player, SCROLL_GO_ON);
9215 ScrollScreen(NULL, SCROLL_GO_ON);
9217 AdvanceFrameAndPlayerCounters(player->index_nr);
9223 player->move_delay_value = original_move_delay_value;
9226 if (player->last_move_dir & MV_HORIZONTAL)
9228 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9229 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9233 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9234 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9240 if (moved & MF_MOVING && !ScreenMovPos &&
9241 (player == local_player || !options.network))
9243 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9244 int offset = (setup.scroll_delay ? 3 : 0);
9246 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9248 /* actual player has left the screen -- scroll in that direction */
9249 if (jx != old_jx) /* player has moved horizontally */
9250 scroll_x += (jx - old_jx);
9251 else /* player has moved vertically */
9252 scroll_y += (jy - old_jy);
9256 if (jx != old_jx) /* player has moved horizontally */
9258 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9259 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9260 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9262 /* don't scroll over playfield boundaries */
9263 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9264 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9266 /* don't scroll more than one field at a time */
9267 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9269 /* don't scroll against the player's moving direction */
9270 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9271 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9272 scroll_x = old_scroll_x;
9274 else /* player has moved vertically */
9276 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9277 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9278 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9280 /* don't scroll over playfield boundaries */
9281 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9282 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9284 /* don't scroll more than one field at a time */
9285 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9287 /* don't scroll against the player's moving direction */
9288 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9289 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9290 scroll_y = old_scroll_y;
9294 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9296 if (!options.network && !AllPlayersInVisibleScreen())
9298 scroll_x = old_scroll_x;
9299 scroll_y = old_scroll_y;
9303 ScrollScreen(player, SCROLL_INIT);
9304 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9309 player->StepFrame = 0;
9311 if (moved & MF_MOVING)
9313 if (old_jx != jx && old_jy == jy)
9314 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9315 else if (old_jx == jx && old_jy != jy)
9316 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9318 DrawLevelField(jx, jy); /* for "crumbled sand" */
9320 player->last_move_dir = player->MovDir;
9321 player->is_moving = TRUE;
9322 player->is_snapping = FALSE;
9323 player->is_switching = FALSE;
9324 player->is_dropping = FALSE;
9328 CheckGravityMovementWhenNotMoving(player);
9330 player->is_moving = FALSE;
9332 /* at this point, the player is allowed to move, but cannot move right now
9333 (e.g. because of something blocking the way) -- ensure that the player
9334 is also allowed to move in the next frame (in old versions before 3.1.1,
9335 the player was forced to wait again for eight frames before next try) */
9337 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9338 player->move_delay = 0; /* allow direct movement in the next frame */
9341 if (player->move_delay == -1) /* not yet initialized by DigField() */
9342 player->move_delay = player->move_delay_value;
9344 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9346 TestIfPlayerTouchesBadThing(jx, jy);
9347 TestIfPlayerTouchesCustomElement(jx, jy);
9350 if (!player->active)
9351 RemovePlayer(player);
9356 void ScrollPlayer(struct PlayerInfo *player, int mode)
9358 int jx = player->jx, jy = player->jy;
9359 int last_jx = player->last_jx, last_jy = player->last_jy;
9360 int move_stepsize = TILEX / player->move_delay_value;
9362 #if USE_NEW_PLAYER_SPEED
9363 if (!player->active)
9366 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9369 if (!player->active || player->MovPos == 0)
9373 if (mode == SCROLL_INIT)
9375 player->actual_frame_counter = FrameCounter;
9376 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9378 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9379 Feld[last_jx][last_jy] == EL_EMPTY)
9381 int last_field_block_delay = 0; /* start with no blocking at all */
9382 int block_delay_adjustment = player->block_delay_adjustment;
9384 /* if player blocks last field, add delay for exactly one move */
9385 if (player->block_last_field)
9387 last_field_block_delay += player->move_delay_value;
9389 /* when blocking enabled, prevent moving up despite gravity */
9390 if (game.gravity && player->MovDir == MV_UP)
9391 block_delay_adjustment = -1;
9394 /* add block delay adjustment (also possible when not blocking) */
9395 last_field_block_delay += block_delay_adjustment;
9397 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9398 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9401 #if USE_NEW_PLAYER_SPEED
9402 if (player->MovPos != 0) /* player has not yet reached destination */
9408 else if (!FrameReached(&player->actual_frame_counter, 1))
9412 printf("::: player->MovPos: %d -> %d\n",
9414 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9417 #if USE_NEW_PLAYER_SPEED
9418 if (player->MovPos != 0)
9420 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9421 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9423 /* before DrawPlayer() to draw correct player graphic for this case */
9424 if (player->MovPos == 0)
9425 CheckGravityMovement(player);
9428 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9429 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9431 /* before DrawPlayer() to draw correct player graphic for this case */
9432 if (player->MovPos == 0)
9433 CheckGravityMovement(player);
9436 if (player->MovPos == 0) /* player reached destination field */
9439 printf("::: player reached destination field\n");
9442 if (player->move_delay_reset_counter > 0)
9444 player->move_delay_reset_counter--;
9446 if (player->move_delay_reset_counter == 0)
9448 /* continue with normal speed after quickly moving through gate */
9449 HALVE_PLAYER_SPEED(player);
9451 /* be able to make the next move without delay */
9452 player->move_delay = 0;
9456 player->last_jx = jx;
9457 player->last_jy = jy;
9459 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9460 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9461 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9463 DrawPlayer(player); /* needed here only to cleanup last field */
9464 RemovePlayer(player);
9466 if (local_player->friends_still_needed == 0 ||
9467 IS_SP_ELEMENT(Feld[jx][jy]))
9468 player->LevelSolved = player->GameOver = TRUE;
9471 /* this breaks one level: "machine", level 000 */
9473 int move_direction = player->MovDir;
9474 int enter_side = MV_DIR_OPPOSITE(move_direction);
9475 int leave_side = move_direction;
9476 int old_jx = last_jx;
9477 int old_jy = last_jy;
9478 int old_element = Feld[old_jx][old_jy];
9479 int new_element = Feld[jx][jy];
9481 if (IS_CUSTOM_ELEMENT(old_element))
9482 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9484 player->index_bit, leave_side);
9486 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9488 player->index_bit, leave_side);
9490 if (IS_CUSTOM_ELEMENT(new_element))
9491 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9492 player->index_bit, enter_side);
9494 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9496 player->index_bit, enter_side);
9498 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9499 CE_MOVE_OF_X, move_direction);
9502 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9504 TestIfPlayerTouchesBadThing(jx, jy);
9505 TestIfPlayerTouchesCustomElement(jx, jy);
9507 /* needed because pushed element has not yet reached its destination,
9508 so it would trigger a change event at its previous field location */
9509 if (!player->is_pushing)
9510 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9512 if (!player->active)
9513 RemovePlayer(player);
9516 if (level.use_step_counter)
9526 if (TimeLeft <= 10 && setup.time_limit)
9527 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9529 DrawGameValue_Time(TimeLeft);
9531 if (!TimeLeft && setup.time_limit)
9532 for (i = 0; i < MAX_PLAYERS; i++)
9533 KillPlayer(&stored_player[i]);
9535 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9536 DrawGameValue_Time(TimePlayed);
9539 if (tape.single_step && tape.recording && !tape.pausing &&
9540 !player->programmed_action)
9541 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9545 void ScrollScreen(struct PlayerInfo *player, int mode)
9547 static unsigned long screen_frame_counter = 0;
9549 if (mode == SCROLL_INIT)
9551 /* set scrolling step size according to actual player's moving speed */
9552 ScrollStepSize = TILEX / player->move_delay_value;
9554 screen_frame_counter = FrameCounter;
9555 ScreenMovDir = player->MovDir;
9556 ScreenMovPos = player->MovPos;
9557 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9560 else if (!FrameReached(&screen_frame_counter, 1))
9565 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9566 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9567 redraw_mask |= REDRAW_FIELD;
9570 ScreenMovDir = MV_NONE;
9573 void TestIfPlayerTouchesCustomElement(int x, int y)
9575 static int xy[4][2] =
9582 static int trigger_sides[4][2] =
9584 /* center side border side */
9585 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9586 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9587 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9588 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9590 static int touch_dir[4] =
9597 int center_element = Feld[x][y]; /* should always be non-moving! */
9600 for (i = 0; i < NUM_DIRECTIONS; i++)
9602 int xx = x + xy[i][0];
9603 int yy = y + xy[i][1];
9604 int center_side = trigger_sides[i][0];
9605 int border_side = trigger_sides[i][1];
9608 if (!IN_LEV_FIELD(xx, yy))
9611 if (IS_PLAYER(x, y))
9613 struct PlayerInfo *player = PLAYERINFO(x, y);
9615 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9616 border_element = Feld[xx][yy]; /* may be moving! */
9617 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9618 border_element = Feld[xx][yy];
9619 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9620 border_element = MovingOrBlocked2Element(xx, yy);
9622 continue; /* center and border element do not touch */
9624 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9625 player->index_bit, border_side);
9626 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9627 CE_PLAYER_TOUCHES_X,
9628 player->index_bit, border_side);
9630 else if (IS_PLAYER(xx, yy))
9632 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9634 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9636 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9637 continue; /* center and border element do not touch */
9640 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9641 player->index_bit, center_side);
9642 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9643 CE_PLAYER_TOUCHES_X,
9644 player->index_bit, center_side);
9650 void TestIfElementTouchesCustomElement(int x, int y)
9652 static int xy[4][2] =
9659 static int trigger_sides[4][2] =
9661 /* center side border side */
9662 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9663 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9664 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9665 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9667 static int touch_dir[4] =
9674 boolean change_center_element = FALSE;
9675 int center_element = Feld[x][y]; /* should always be non-moving! */
9678 for (i = 0; i < NUM_DIRECTIONS; i++)
9680 int xx = x + xy[i][0];
9681 int yy = y + xy[i][1];
9682 int center_side = trigger_sides[i][0];
9683 int border_side = trigger_sides[i][1];
9686 if (!IN_LEV_FIELD(xx, yy))
9689 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9690 border_element = Feld[xx][yy]; /* may be moving! */
9691 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9692 border_element = Feld[xx][yy];
9693 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9694 border_element = MovingOrBlocked2Element(xx, yy);
9696 continue; /* center and border element do not touch */
9698 /* check for change of center element (but change it only once) */
9699 if (!change_center_element)
9700 change_center_element =
9701 CheckElementChangeBySide(x, y, center_element, border_element,
9702 CE_TOUCHING_X, border_side);
9704 /* check for change of border element */
9705 CheckElementChangeBySide(xx, yy, border_element, center_element,
9706 CE_TOUCHING_X, center_side);
9710 void TestIfElementHitsCustomElement(int x, int y, int direction)
9712 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9713 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9714 int hitx = x + dx, hity = y + dy;
9715 int hitting_element = Feld[x][y];
9716 int touched_element;
9718 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9721 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9722 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9724 if (IN_LEV_FIELD(hitx, hity))
9726 int opposite_direction = MV_DIR_OPPOSITE(direction);
9727 int hitting_side = direction;
9728 int touched_side = opposite_direction;
9729 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9730 MovDir[hitx][hity] != direction ||
9731 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9737 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9738 CE_HITTING_X, touched_side);
9740 CheckElementChangeBySide(hitx, hity, touched_element,
9741 hitting_element, CE_HIT_BY_X, hitting_side);
9743 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9744 CE_HIT_BY_SOMETHING, opposite_direction);
9748 /* "hitting something" is also true when hitting the playfield border */
9749 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9750 CE_HITTING_SOMETHING, direction);
9754 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9756 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9757 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9758 int hitx = x + dx, hity = y + dy;
9759 int hitting_element = Feld[x][y];
9760 int touched_element;
9762 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9763 !IS_FREE(hitx, hity) &&
9764 (!IS_MOVING(hitx, hity) ||
9765 MovDir[hitx][hity] != direction ||
9766 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9769 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9773 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9777 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9778 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9780 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9781 EP_CAN_SMASH_EVERYTHING, direction);
9783 if (IN_LEV_FIELD(hitx, hity))
9785 int opposite_direction = MV_DIR_OPPOSITE(direction);
9786 int hitting_side = direction;
9787 int touched_side = opposite_direction;
9789 int touched_element = MovingOrBlocked2Element(hitx, hity);
9792 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9793 MovDir[hitx][hity] != direction ||
9794 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9803 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9804 CE_SMASHED_BY_SOMETHING, opposite_direction);
9806 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9807 CE_OTHER_IS_SMASHING, touched_side);
9809 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9810 CE_OTHER_GETS_SMASHED, hitting_side);
9816 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9818 int i, kill_x = -1, kill_y = -1;
9819 int bad_element = -1;
9820 static int test_xy[4][2] =
9827 static int test_dir[4] =
9835 for (i = 0; i < NUM_DIRECTIONS; i++)
9837 int test_x, test_y, test_move_dir, test_element;
9839 test_x = good_x + test_xy[i][0];
9840 test_y = good_y + test_xy[i][1];
9842 if (!IN_LEV_FIELD(test_x, test_y))
9846 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9848 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9850 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9851 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9853 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9854 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9858 bad_element = test_element;
9864 if (kill_x != -1 || kill_y != -1)
9866 if (IS_PLAYER(good_x, good_y))
9868 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9870 if (player->shield_deadly_time_left > 0 &&
9871 !IS_INDESTRUCTIBLE(bad_element))
9872 Bang(kill_x, kill_y);
9873 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9877 Bang(good_x, good_y);
9881 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9883 int i, kill_x = -1, kill_y = -1;
9884 int bad_element = Feld[bad_x][bad_y];
9885 static int test_xy[4][2] =
9892 static int touch_dir[4] =
9899 static int test_dir[4] =
9907 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9910 for (i = 0; i < NUM_DIRECTIONS; i++)
9912 int test_x, test_y, test_move_dir, test_element;
9914 test_x = bad_x + test_xy[i][0];
9915 test_y = bad_y + test_xy[i][1];
9916 if (!IN_LEV_FIELD(test_x, test_y))
9920 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9922 test_element = Feld[test_x][test_y];
9924 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9925 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9927 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9928 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9930 /* good thing is player or penguin that does not move away */
9931 if (IS_PLAYER(test_x, test_y))
9933 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9935 if (bad_element == EL_ROBOT && player->is_moving)
9936 continue; /* robot does not kill player if he is moving */
9938 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9940 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9941 continue; /* center and border element do not touch */
9948 else if (test_element == EL_PENGUIN)
9957 if (kill_x != -1 || kill_y != -1)
9959 if (IS_PLAYER(kill_x, kill_y))
9961 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9963 if (player->shield_deadly_time_left > 0 &&
9964 !IS_INDESTRUCTIBLE(bad_element))
9966 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9970 Bang(kill_x, kill_y);
9974 void TestIfPlayerTouchesBadThing(int x, int y)
9976 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9979 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9981 TestIfGoodThingHitsBadThing(x, y, move_dir);
9984 void TestIfBadThingTouchesPlayer(int x, int y)
9986 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9989 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9991 TestIfBadThingHitsGoodThing(x, y, move_dir);
9994 void TestIfFriendTouchesBadThing(int x, int y)
9996 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9999 void TestIfBadThingTouchesFriend(int x, int y)
10001 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10004 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10006 int i, kill_x = bad_x, kill_y = bad_y;
10007 static int xy[4][2] =
10015 for (i = 0; i < NUM_DIRECTIONS; i++)
10019 x = bad_x + xy[i][0];
10020 y = bad_y + xy[i][1];
10021 if (!IN_LEV_FIELD(x, y))
10024 element = Feld[x][y];
10025 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10026 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10034 if (kill_x != bad_x || kill_y != bad_y)
10035 Bang(bad_x, bad_y);
10038 void KillPlayer(struct PlayerInfo *player)
10040 int jx = player->jx, jy = player->jy;
10042 if (!player->active)
10045 /* remove accessible field at the player's position */
10046 Feld[jx][jy] = EL_EMPTY;
10048 /* deactivate shield (else Bang()/Explode() would not work right) */
10049 player->shield_normal_time_left = 0;
10050 player->shield_deadly_time_left = 0;
10053 BuryPlayer(player);
10056 static void KillPlayerUnlessEnemyProtected(int x, int y)
10058 if (!PLAYER_ENEMY_PROTECTED(x, y))
10059 KillPlayer(PLAYERINFO(x, y));
10062 static void KillPlayerUnlessExplosionProtected(int x, int y)
10064 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10065 KillPlayer(PLAYERINFO(x, y));
10068 void BuryPlayer(struct PlayerInfo *player)
10070 int jx = player->jx, jy = player->jy;
10072 if (!player->active)
10075 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10076 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10078 player->GameOver = TRUE;
10079 RemovePlayer(player);
10082 void RemovePlayer(struct PlayerInfo *player)
10084 int jx = player->jx, jy = player->jy;
10085 int i, found = FALSE;
10087 player->present = FALSE;
10088 player->active = FALSE;
10090 if (!ExplodeField[jx][jy])
10091 StorePlayer[jx][jy] = 0;
10093 if (player->is_moving)
10094 DrawLevelField(player->last_jx, player->last_jy);
10096 for (i = 0; i < MAX_PLAYERS; i++)
10097 if (stored_player[i].active)
10101 AllPlayersGone = TRUE;
10107 #if USE_NEW_SNAP_DELAY
10108 static void setFieldForSnapping(int x, int y, int element, int direction)
10110 struct ElementInfo *ei = &element_info[element];
10111 int direction_bit = MV_DIR_BIT(direction);
10112 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10113 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10114 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10116 Feld[x][y] = EL_ELEMENT_SNAPPING;
10117 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10119 ResetGfxAnimation(x, y);
10121 GfxElement[x][y] = element;
10122 GfxAction[x][y] = action;
10123 GfxDir[x][y] = direction;
10124 GfxFrame[x][y] = -1;
10129 =============================================================================
10130 checkDiagonalPushing()
10131 -----------------------------------------------------------------------------
10132 check if diagonal input device direction results in pushing of object
10133 (by checking if the alternative direction is walkable, diggable, ...)
10134 =============================================================================
10137 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10138 int x, int y, int real_dx, int real_dy)
10140 int jx, jy, dx, dy, xx, yy;
10142 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10145 /* diagonal direction: check alternative direction */
10150 xx = jx + (dx == 0 ? real_dx : 0);
10151 yy = jy + (dy == 0 ? real_dy : 0);
10153 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10157 =============================================================================
10159 -----------------------------------------------------------------------------
10160 x, y: field next to player (non-diagonal) to try to dig to
10161 real_dx, real_dy: direction as read from input device (can be diagonal)
10162 =============================================================================
10165 int DigField(struct PlayerInfo *player,
10166 int oldx, int oldy, int x, int y,
10167 int real_dx, int real_dy, int mode)
10169 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10170 boolean player_was_pushing = player->is_pushing;
10171 int jx = oldx, jy = oldy;
10172 int dx = x - jx, dy = y - jy;
10173 int nextx = x + dx, nexty = y + dy;
10174 int move_direction = (dx == -1 ? MV_LEFT :
10175 dx == +1 ? MV_RIGHT :
10177 dy == +1 ? MV_DOWN : MV_NONE);
10178 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10179 int dig_side = MV_DIR_OPPOSITE(move_direction);
10180 int old_element = Feld[jx][jy];
10184 if (is_player) /* function can also be called by EL_PENGUIN */
10186 if (player->MovPos == 0)
10188 player->is_digging = FALSE;
10189 player->is_collecting = FALSE;
10192 if (player->MovPos == 0) /* last pushing move finished */
10193 player->is_pushing = FALSE;
10195 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10197 player->is_switching = FALSE;
10198 player->push_delay = -1;
10200 return MF_NO_ACTION;
10204 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10205 return MF_NO_ACTION;
10207 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10208 old_element = Back[jx][jy];
10210 /* in case of element dropped at player position, check background */
10211 else if (Back[jx][jy] != EL_EMPTY &&
10212 game.engine_version >= VERSION_IDENT(2,2,0,0))
10213 old_element = Back[jx][jy];
10215 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10216 return MF_NO_ACTION; /* field has no opening in this direction */
10218 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10219 return MF_NO_ACTION; /* field has no opening in this direction */
10221 element = Feld[x][y];
10222 #if USE_NEW_CUSTOM_VALUE
10225 collect_count = element_info[element].collect_count_initial;
10227 collect_count = CustomValue[x][y];
10231 collect_count = element_info[element].collect_count_initial;
10235 if (element != EL_BLOCKED &&
10236 CustomValue[x][y] != element_info[element].collect_count_initial)
10237 printf("::: %d: %d != %d\n",
10240 element_info[element].collect_count_initial);
10243 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10244 return MF_NO_ACTION;
10246 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10247 game.engine_version >= VERSION_IDENT(2,2,0,0))
10249 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10250 player->index_bit, dig_side);
10251 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10252 player->index_bit, dig_side);
10254 if (Feld[x][y] != element) /* field changed by snapping */
10257 return MF_NO_ACTION;
10260 if (game.gravity && is_player && !player->is_auto_moving &&
10261 canFallDown(player) && move_direction != MV_DOWN &&
10262 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10263 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10265 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10267 int sound_element = SND_ELEMENT(element);
10268 int sound_action = ACTION_WALKING;
10270 if (IS_RND_GATE(element))
10272 if (!player->key[RND_GATE_NR(element)])
10273 return MF_NO_ACTION;
10275 else if (IS_RND_GATE_GRAY(element))
10277 if (!player->key[RND_GATE_GRAY_NR(element)])
10278 return MF_NO_ACTION;
10280 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10282 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10283 return MF_NO_ACTION;
10285 else if (element == EL_EXIT_OPEN ||
10286 element == EL_SP_EXIT_OPEN ||
10287 element == EL_SP_EXIT_OPENING)
10289 sound_action = ACTION_PASSING; /* player is passing exit */
10291 else if (element == EL_EMPTY)
10293 sound_action = ACTION_MOVING; /* nothing to walk on */
10296 /* play sound from background or player, whatever is available */
10297 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10298 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10300 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10302 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10304 if (!ACCESS_FROM(element, opposite_direction))
10305 return MF_NO_ACTION; /* field not accessible from this direction */
10307 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10308 return MF_NO_ACTION;
10310 if (IS_EM_GATE(element))
10312 if (!player->key[EM_GATE_NR(element)])
10313 return MF_NO_ACTION;
10315 else if (IS_EM_GATE_GRAY(element))
10317 if (!player->key[EM_GATE_GRAY_NR(element)])
10318 return MF_NO_ACTION;
10320 else if (IS_EM_GATE_GRAY_ACTIVE(element))
10322 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10323 return MF_NO_ACTION;
10325 else if (IS_SP_PORT(element))
10327 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10328 element == EL_SP_GRAVITY_PORT_RIGHT ||
10329 element == EL_SP_GRAVITY_PORT_UP ||
10330 element == EL_SP_GRAVITY_PORT_DOWN)
10331 game.gravity = !game.gravity;
10332 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10333 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10334 element == EL_SP_GRAVITY_ON_PORT_UP ||
10335 element == EL_SP_GRAVITY_ON_PORT_DOWN)
10336 game.gravity = TRUE;
10337 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10338 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10339 element == EL_SP_GRAVITY_OFF_PORT_UP ||
10340 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10341 game.gravity = FALSE;
10344 /* automatically move to the next field with double speed */
10345 player->programmed_action = move_direction;
10347 if (player->move_delay_reset_counter == 0)
10349 player->move_delay_reset_counter = 2; /* two double speed steps */
10351 DOUBLE_PLAYER_SPEED(player);
10354 PlayLevelSoundAction(x, y, ACTION_PASSING);
10356 else if (IS_DIGGABLE(element))
10360 if (mode != DF_SNAP)
10362 GfxElement[x][y] = GFX_ELEMENT(element);
10363 player->is_digging = TRUE;
10366 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10368 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10369 player->index_bit, dig_side);
10371 if (mode == DF_SNAP)
10373 #if USE_NEW_SNAP_DELAY
10374 if (level.block_snap_field)
10375 setFieldForSnapping(x, y, element, move_direction);
10377 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10379 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10382 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10383 player->index_bit, dig_side);
10386 else if (IS_COLLECTIBLE(element))
10390 if (is_player && mode != DF_SNAP)
10392 GfxElement[x][y] = element;
10393 player->is_collecting = TRUE;
10396 if (element == EL_SPEED_PILL)
10398 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10400 else if (element == EL_EXTRA_TIME && level.time > 0)
10402 TimeLeft += level.extra_time;
10403 DrawGameValue_Time(TimeLeft);
10405 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10407 player->shield_normal_time_left += level.shield_normal_time;
10408 if (element == EL_SHIELD_DEADLY)
10409 player->shield_deadly_time_left += level.shield_deadly_time;
10411 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10413 if (player->inventory_size < MAX_INVENTORY_SIZE)
10414 player->inventory_element[player->inventory_size++] = element;
10416 DrawGameValue_Dynamite(local_player->inventory_size);
10418 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10420 player->dynabomb_count++;
10421 player->dynabombs_left++;
10423 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10425 player->dynabomb_size++;
10427 else if (element == EL_DYNABOMB_INCREASE_POWER)
10429 player->dynabomb_xl = TRUE;
10431 else if (IS_KEY(element))
10433 player->key[KEY_NR(element)] = TRUE;
10435 DrawGameValue_Keys(player->key);
10437 redraw_mask |= REDRAW_DOOR_1;
10439 else if (IS_ENVELOPE(element))
10441 player->show_envelope = element;
10443 else if (element == EL_EMC_LENSES)
10445 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10447 RedrawAllInvisibleElementsForLenses();
10449 else if (element == EL_EMC_MAGNIFIER)
10451 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10453 RedrawAllInvisibleElementsForMagnifier();
10455 else if (IS_DROPPABLE(element) ||
10456 IS_THROWABLE(element)) /* can be collected and dropped */
10460 if (collect_count == 0)
10461 player->inventory_infinite_element = element;
10463 for (i = 0; i < collect_count; i++)
10464 if (player->inventory_size < MAX_INVENTORY_SIZE)
10465 player->inventory_element[player->inventory_size++] = element;
10467 DrawGameValue_Dynamite(local_player->inventory_size);
10469 else if (collect_count > 0)
10471 local_player->gems_still_needed -= collect_count;
10472 if (local_player->gems_still_needed < 0)
10473 local_player->gems_still_needed = 0;
10475 DrawGameValue_Emeralds(local_player->gems_still_needed);
10478 RaiseScoreElement(element);
10479 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10482 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10483 player->index_bit, dig_side);
10485 if (mode == DF_SNAP)
10487 #if USE_NEW_SNAP_DELAY
10488 if (level.block_snap_field)
10489 setFieldForSnapping(x, y, element, move_direction);
10491 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10493 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10496 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10497 player->index_bit, dig_side);
10500 else if (IS_PUSHABLE(element))
10502 if (mode == DF_SNAP && element != EL_BD_ROCK)
10503 return MF_NO_ACTION;
10505 if (CAN_FALL(element) && dy)
10506 return MF_NO_ACTION;
10508 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10509 !(element == EL_SPRING && level.use_spring_bug))
10510 return MF_NO_ACTION;
10512 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10513 ((move_direction & MV_VERTICAL &&
10514 ((element_info[element].move_pattern & MV_LEFT &&
10515 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10516 (element_info[element].move_pattern & MV_RIGHT &&
10517 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10518 (move_direction & MV_HORIZONTAL &&
10519 ((element_info[element].move_pattern & MV_UP &&
10520 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10521 (element_info[element].move_pattern & MV_DOWN &&
10522 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10523 return MF_NO_ACTION;
10525 /* do not push elements already moving away faster than player */
10526 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10527 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10528 return MF_NO_ACTION;
10530 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10532 if (player->push_delay_value == -1 || !player_was_pushing)
10533 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10535 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10537 if (player->push_delay_value == -1)
10538 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10540 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10542 if (!player->is_pushing)
10543 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10546 player->is_pushing = TRUE;
10548 if (!(IN_LEV_FIELD(nextx, nexty) &&
10549 (IS_FREE(nextx, nexty) ||
10550 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10551 IS_SB_ELEMENT(element)))))
10552 return MF_NO_ACTION;
10554 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10555 return MF_NO_ACTION;
10557 if (player->push_delay == -1) /* new pushing; restart delay */
10558 player->push_delay = 0;
10560 if (player->push_delay < player->push_delay_value &&
10561 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10562 element != EL_SPRING && element != EL_BALLOON)
10564 /* make sure that there is no move delay before next try to push */
10565 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10566 player->move_delay = 0;
10568 return MF_NO_ACTION;
10571 if (IS_SB_ELEMENT(element))
10573 if (element == EL_SOKOBAN_FIELD_FULL)
10575 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10576 local_player->sokobanfields_still_needed++;
10579 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10581 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10582 local_player->sokobanfields_still_needed--;
10585 Feld[x][y] = EL_SOKOBAN_OBJECT;
10587 if (Back[x][y] == Back[nextx][nexty])
10588 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10589 else if (Back[x][y] != 0)
10590 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10593 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10596 if (local_player->sokobanfields_still_needed == 0 &&
10597 game.emulation == EMU_SOKOBAN)
10599 player->LevelSolved = player->GameOver = TRUE;
10600 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10604 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10606 InitMovingField(x, y, move_direction);
10607 GfxAction[x][y] = ACTION_PUSHING;
10609 if (mode == DF_SNAP)
10610 ContinueMoving(x, y);
10612 MovPos[x][y] = (dx != 0 ? dx : dy);
10614 Pushed[x][y] = TRUE;
10615 Pushed[nextx][nexty] = TRUE;
10617 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10618 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10620 player->push_delay_value = -1; /* get new value later */
10622 /* check for element change _after_ element has been pushed */
10623 if (game.use_change_when_pushing_bug)
10625 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10626 player->index_bit, dig_side);
10627 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10628 player->index_bit, dig_side);
10631 else if (IS_SWITCHABLE(element))
10633 if (PLAYER_SWITCHING(player, x, y))
10635 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10636 player->index_bit, dig_side);
10641 player->is_switching = TRUE;
10642 player->switch_x = x;
10643 player->switch_y = y;
10645 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10647 if (element == EL_ROBOT_WHEEL)
10649 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10653 DrawLevelField(x, y);
10655 else if (element == EL_SP_TERMINAL)
10660 SCAN_PLAYFIELD(xx, yy)
10662 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10665 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10667 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10668 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10671 else if (IS_BELT_SWITCH(element))
10673 ToggleBeltSwitch(x, y);
10675 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10676 element == EL_SWITCHGATE_SWITCH_DOWN)
10678 ToggleSwitchgateSwitch(x, y);
10680 else if (element == EL_LIGHT_SWITCH ||
10681 element == EL_LIGHT_SWITCH_ACTIVE)
10683 ToggleLightSwitch(x, y);
10685 else if (element == EL_TIMEGATE_SWITCH)
10687 ActivateTimegateSwitch(x, y);
10689 else if (element == EL_BALLOON_SWITCH_LEFT ||
10690 element == EL_BALLOON_SWITCH_RIGHT ||
10691 element == EL_BALLOON_SWITCH_UP ||
10692 element == EL_BALLOON_SWITCH_DOWN ||
10693 element == EL_BALLOON_SWITCH_NONE ||
10694 element == EL_BALLOON_SWITCH_ANY)
10696 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10697 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10698 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10699 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10700 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10703 else if (element == EL_LAMP)
10705 Feld[x][y] = EL_LAMP_ACTIVE;
10706 local_player->lights_still_needed--;
10708 ResetGfxAnimation(x, y);
10709 DrawLevelField(x, y);
10711 else if (element == EL_TIME_ORB_FULL)
10713 Feld[x][y] = EL_TIME_ORB_EMPTY;
10715 if (level.time > 0 || level.use_time_orb_bug)
10717 TimeLeft += level.time_orb_time;
10718 DrawGameValue_Time(TimeLeft);
10721 ResetGfxAnimation(x, y);
10722 DrawLevelField(x, y);
10724 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
10725 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10729 game.ball_state = !game.ball_state;
10732 SCAN_PLAYFIELD(xx, yy)
10734 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10737 int e = Feld[xx][yy];
10739 if (game.ball_state)
10741 if (e == EL_EMC_MAGIC_BALL)
10742 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
10743 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
10744 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
10748 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
10749 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
10750 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10751 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
10756 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10757 player->index_bit, dig_side);
10759 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10760 player->index_bit, dig_side);
10762 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10763 player->index_bit, dig_side);
10769 if (!PLAYER_SWITCHING(player, x, y))
10771 player->is_switching = TRUE;
10772 player->switch_x = x;
10773 player->switch_y = y;
10775 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10776 player->index_bit, dig_side);
10777 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10778 player->index_bit, dig_side);
10780 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
10781 player->index_bit, dig_side);
10782 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10783 player->index_bit, dig_side);
10786 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10787 player->index_bit, dig_side);
10788 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10789 player->index_bit, dig_side);
10791 return MF_NO_ACTION;
10794 player->push_delay = -1;
10796 if (is_player) /* function can also be called by EL_PENGUIN */
10798 if (Feld[x][y] != element) /* really digged/collected something */
10799 player->is_collecting = !player->is_digging;
10805 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10807 int jx = player->jx, jy = player->jy;
10808 int x = jx + dx, y = jy + dy;
10809 int snap_direction = (dx == -1 ? MV_LEFT :
10810 dx == +1 ? MV_RIGHT :
10812 dy == +1 ? MV_DOWN : MV_NONE);
10814 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10817 if (!player->active || !IN_LEV_FIELD(x, y))
10825 if (player->MovPos == 0)
10826 player->is_pushing = FALSE;
10828 player->is_snapping = FALSE;
10830 if (player->MovPos == 0)
10832 player->is_moving = FALSE;
10833 player->is_digging = FALSE;
10834 player->is_collecting = FALSE;
10840 if (player->is_snapping)
10843 player->MovDir = snap_direction;
10845 if (player->MovPos == 0)
10847 player->is_moving = FALSE;
10848 player->is_digging = FALSE;
10849 player->is_collecting = FALSE;
10852 player->is_dropping = FALSE;
10854 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10857 player->is_snapping = TRUE;
10859 if (player->MovPos == 0)
10861 player->is_moving = FALSE;
10862 player->is_digging = FALSE;
10863 player->is_collecting = FALSE;
10866 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10867 DrawLevelField(player->last_jx, player->last_jy);
10869 DrawLevelField(x, y);
10874 boolean DropElement(struct PlayerInfo *player)
10876 int old_element, new_element;
10877 int dropx = player->jx, dropy = player->jy;
10878 int drop_direction = player->MovDir;
10879 int drop_side = drop_direction;
10880 int drop_element = (player->inventory_size > 0 ?
10881 player->inventory_element[player->inventory_size - 1] :
10882 player->inventory_infinite_element != EL_UNDEFINED ?
10883 player->inventory_infinite_element :
10884 player->dynabombs_left > 0 ?
10885 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10888 /* do not drop an element on top of another element; when holding drop key
10889 pressed without moving, dropped element must move away before the next
10890 element can be dropped (this is especially important if the next element
10891 is dynamite, which can be placed on background for historical reasons) */
10892 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10895 if (IS_THROWABLE(drop_element))
10897 dropx += GET_DX_FROM_DIR(drop_direction);
10898 dropy += GET_DY_FROM_DIR(drop_direction);
10900 if (!IN_LEV_FIELD(dropx, dropy))
10904 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10905 new_element = drop_element; /* default: no change when dropping */
10907 /* check if player is active, not moving and ready to drop */
10908 if (!player->active || player->MovPos || player->drop_delay > 0)
10911 /* check if player has anything that can be dropped */
10912 if (new_element == EL_UNDEFINED)
10915 /* check if anything can be dropped at the current position */
10916 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10919 /* collected custom elements can only be dropped on empty fields */
10920 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10923 if (old_element != EL_EMPTY)
10924 Back[dropx][dropy] = old_element; /* store old element on this field */
10926 ResetGfxAnimation(dropx, dropy);
10927 ResetRandomAnimationValue(dropx, dropy);
10929 if (player->inventory_size > 0 ||
10930 player->inventory_infinite_element != EL_UNDEFINED)
10932 if (player->inventory_size > 0)
10934 player->inventory_size--;
10936 DrawGameValue_Dynamite(local_player->inventory_size);
10938 if (new_element == EL_DYNAMITE)
10939 new_element = EL_DYNAMITE_ACTIVE;
10940 else if (new_element == EL_SP_DISK_RED)
10941 new_element = EL_SP_DISK_RED_ACTIVE;
10944 Feld[dropx][dropy] = new_element;
10946 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10947 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10948 el2img(Feld[dropx][dropy]), 0);
10950 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10952 /* needed if previous element just changed to "empty" in the last frame */
10953 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
10955 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10956 player->index_bit, drop_side);
10957 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10959 player->index_bit, drop_side);
10961 TestIfElementTouchesCustomElement(dropx, dropy);
10963 else /* player is dropping a dyna bomb */
10965 player->dynabombs_left--;
10967 Feld[dropx][dropy] = new_element;
10969 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10970 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10971 el2img(Feld[dropx][dropy]), 0);
10973 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10976 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10977 InitField_WithBug1(dropx, dropy, FALSE);
10979 new_element = Feld[dropx][dropy]; /* element might have changed */
10981 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10982 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10984 int move_direction, nextx, nexty;
10986 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10987 MovDir[dropx][dropy] = drop_direction;
10989 move_direction = MovDir[dropx][dropy];
10990 nextx = dropx + GET_DX_FROM_DIR(move_direction);
10991 nexty = dropy + GET_DY_FROM_DIR(move_direction);
10993 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
10994 CheckCollision[dropx][dropy] = 2;
10997 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10998 player->is_dropping = TRUE;
11000 player->drop_x = dropx;
11001 player->drop_y = dropy;
11006 /* ------------------------------------------------------------------------- */
11007 /* game sound playing functions */
11008 /* ------------------------------------------------------------------------- */
11010 static int *loop_sound_frame = NULL;
11011 static int *loop_sound_volume = NULL;
11013 void InitPlayLevelSound()
11015 int num_sounds = getSoundListSize();
11017 checked_free(loop_sound_frame);
11018 checked_free(loop_sound_volume);
11020 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11021 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11024 static void PlayLevelSound(int x, int y, int nr)
11026 int sx = SCREENX(x), sy = SCREENY(y);
11027 int volume, stereo_position;
11028 int max_distance = 8;
11029 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11031 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11032 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11035 if (!IN_LEV_FIELD(x, y) ||
11036 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11037 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11040 volume = SOUND_MAX_VOLUME;
11042 if (!IN_SCR_FIELD(sx, sy))
11044 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11045 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11047 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11050 stereo_position = (SOUND_MAX_LEFT +
11051 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11052 (SCR_FIELDX + 2 * max_distance));
11054 if (IS_LOOP_SOUND(nr))
11056 /* This assures that quieter loop sounds do not overwrite louder ones,
11057 while restarting sound volume comparison with each new game frame. */
11059 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11062 loop_sound_volume[nr] = volume;
11063 loop_sound_frame[nr] = FrameCounter;
11066 PlaySoundExt(nr, volume, stereo_position, type);
11069 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11071 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11072 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11073 y < LEVELY(BY1) ? LEVELY(BY1) :
11074 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11078 static void PlayLevelSoundAction(int x, int y, int action)
11080 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11083 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11085 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11087 if (sound_effect != SND_UNDEFINED)
11088 PlayLevelSound(x, y, sound_effect);
11091 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11094 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11096 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11097 PlayLevelSound(x, y, sound_effect);
11100 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11102 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11104 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11105 PlayLevelSound(x, y, sound_effect);
11108 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11110 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11112 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11113 StopSound(sound_effect);
11116 static void PlayLevelMusic()
11118 if (levelset.music[level_nr] != MUS_UNDEFINED)
11119 PlayMusic(levelset.music[level_nr]); /* from config file */
11121 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11124 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11126 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11131 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11135 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11139 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11143 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11147 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11151 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11155 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11158 case SAMPLE_android_clone:
11159 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11162 case SAMPLE_android_move:
11163 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11166 case SAMPLE_spring:
11167 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11171 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
11175 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11178 case SAMPLE_eater_eat:
11179 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11183 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11186 case SAMPLE_collect:
11187 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11190 case SAMPLE_diamond:
11191 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11194 case SAMPLE_squash:
11195 /* !!! CHECK THIS !!! */
11197 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11199 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11203 case SAMPLE_wonderfall:
11204 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11208 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11212 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11216 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11220 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11224 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11228 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11231 case SAMPLE_wonder:
11232 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11236 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11239 case SAMPLE_exit_open:
11240 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11243 case SAMPLE_exit_leave:
11244 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11247 case SAMPLE_dynamite:
11248 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11252 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11256 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11260 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11264 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11268 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11272 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11276 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11281 void RaiseScore(int value)
11283 local_player->score += value;
11285 DrawGameValue_Score(local_player->score);
11288 void RaiseScoreElement(int element)
11293 case EL_BD_DIAMOND:
11294 case EL_EMERALD_YELLOW:
11295 case EL_EMERALD_RED:
11296 case EL_EMERALD_PURPLE:
11297 case EL_SP_INFOTRON:
11298 RaiseScore(level.score[SC_EMERALD]);
11301 RaiseScore(level.score[SC_DIAMOND]);
11304 RaiseScore(level.score[SC_CRYSTAL]);
11307 RaiseScore(level.score[SC_PEARL]);
11310 case EL_BD_BUTTERFLY:
11311 case EL_SP_ELECTRON:
11312 RaiseScore(level.score[SC_BUG]);
11315 case EL_BD_FIREFLY:
11316 case EL_SP_SNIKSNAK:
11317 RaiseScore(level.score[SC_SPACESHIP]);
11320 case EL_DARK_YAMYAM:
11321 RaiseScore(level.score[SC_YAMYAM]);
11324 RaiseScore(level.score[SC_ROBOT]);
11327 RaiseScore(level.score[SC_PACMAN]);
11330 RaiseScore(level.score[SC_NUT]);
11333 case EL_SP_DISK_RED:
11334 case EL_DYNABOMB_INCREASE_NUMBER:
11335 case EL_DYNABOMB_INCREASE_SIZE:
11336 case EL_DYNABOMB_INCREASE_POWER:
11337 RaiseScore(level.score[SC_DYNAMITE]);
11339 case EL_SHIELD_NORMAL:
11340 case EL_SHIELD_DEADLY:
11341 RaiseScore(level.score[SC_SHIELD]);
11343 case EL_EXTRA_TIME:
11344 RaiseScore(level.extra_time_score);
11358 RaiseScore(level.score[SC_KEY]);
11361 RaiseScore(element_info[element].collect_score);
11366 void RequestQuitGame(boolean ask_if_really_quit)
11368 if (AllPlayersGone ||
11369 !ask_if_really_quit ||
11370 level_editor_test_game ||
11371 Request("Do you really want to quit the game ?",
11372 REQ_ASK | REQ_STAY_CLOSED))
11374 #if defined(NETWORK_AVALIABLE)
11375 if (options.network)
11376 SendToServer_StopPlaying();
11380 game_status = GAME_MODE_MAIN;
11386 if (tape.playing && tape.deactivate_display)
11387 TapeDeactivateDisplayOff(TRUE);
11389 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11391 if (tape.playing && tape.deactivate_display)
11392 TapeDeactivateDisplayOn();
11397 /* ---------- new game button stuff ---------------------------------------- */
11399 /* graphic position values for game buttons */
11400 #define GAME_BUTTON_XSIZE 30
11401 #define GAME_BUTTON_YSIZE 30
11402 #define GAME_BUTTON_XPOS 5
11403 #define GAME_BUTTON_YPOS 215
11404 #define SOUND_BUTTON_XPOS 5
11405 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11407 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11408 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11409 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11410 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11411 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11412 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11419 } gamebutton_info[NUM_GAME_BUTTONS] =
11422 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11427 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11428 GAME_CTRL_ID_PAUSE,
11432 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11437 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11438 SOUND_CTRL_ID_MUSIC,
11439 "background music on/off"
11442 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11443 SOUND_CTRL_ID_LOOPS,
11444 "sound loops on/off"
11447 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11448 SOUND_CTRL_ID_SIMPLE,
11449 "normal sounds on/off"
11453 void CreateGameButtons()
11457 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11459 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11460 struct GadgetInfo *gi;
11463 unsigned long event_mask;
11464 int gd_xoffset, gd_yoffset;
11465 int gd_x1, gd_x2, gd_y1, gd_y2;
11468 gd_xoffset = gamebutton_info[i].x;
11469 gd_yoffset = gamebutton_info[i].y;
11470 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11471 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11473 if (id == GAME_CTRL_ID_STOP ||
11474 id == GAME_CTRL_ID_PAUSE ||
11475 id == GAME_CTRL_ID_PLAY)
11477 button_type = GD_TYPE_NORMAL_BUTTON;
11479 event_mask = GD_EVENT_RELEASED;
11480 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11481 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11485 button_type = GD_TYPE_CHECK_BUTTON;
11487 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11488 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11489 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11490 event_mask = GD_EVENT_PRESSED;
11491 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11492 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11495 gi = CreateGadget(GDI_CUSTOM_ID, id,
11496 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11497 GDI_X, DX + gd_xoffset,
11498 GDI_Y, DY + gd_yoffset,
11499 GDI_WIDTH, GAME_BUTTON_XSIZE,
11500 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11501 GDI_TYPE, button_type,
11502 GDI_STATE, GD_BUTTON_UNPRESSED,
11503 GDI_CHECKED, checked,
11504 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11505 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11506 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11507 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11508 GDI_EVENT_MASK, event_mask,
11509 GDI_CALLBACK_ACTION, HandleGameButtons,
11513 Error(ERR_EXIT, "cannot create gadget");
11515 game_gadget[id] = gi;
11519 void FreeGameButtons()
11523 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11524 FreeGadget(game_gadget[i]);
11527 static void MapGameButtons()
11531 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11532 MapGadget(game_gadget[i]);
11535 void UnmapGameButtons()
11539 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11540 UnmapGadget(game_gadget[i]);
11543 static void HandleGameButtons(struct GadgetInfo *gi)
11545 int id = gi->custom_id;
11547 if (game_status != GAME_MODE_PLAYING)
11552 case GAME_CTRL_ID_STOP:
11553 RequestQuitGame(TRUE);
11556 case GAME_CTRL_ID_PAUSE:
11557 if (options.network)
11559 #if defined(NETWORK_AVALIABLE)
11561 SendToServer_ContinuePlaying();
11563 SendToServer_PausePlaying();
11567 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11570 case GAME_CTRL_ID_PLAY:
11573 #if defined(NETWORK_AVALIABLE)
11574 if (options.network)
11575 SendToServer_ContinuePlaying();
11579 tape.pausing = FALSE;
11580 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11585 case SOUND_CTRL_ID_MUSIC:
11586 if (setup.sound_music)
11588 setup.sound_music = FALSE;
11591 else if (audio.music_available)
11593 setup.sound = setup.sound_music = TRUE;
11595 SetAudioMode(setup.sound);
11601 case SOUND_CTRL_ID_LOOPS:
11602 if (setup.sound_loops)
11603 setup.sound_loops = FALSE;
11604 else if (audio.loops_available)
11606 setup.sound = setup.sound_loops = TRUE;
11607 SetAudioMode(setup.sound);
11611 case SOUND_CTRL_ID_SIMPLE:
11612 if (setup.sound_simple)
11613 setup.sound_simple = FALSE;
11614 else if (audio.sound_available)
11616 setup.sound = setup.sound_simple = TRUE;
11617 SetAudioMode(setup.sound);