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 static int get_move_delay_from_stepsize(int move_stepsize)
660 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
662 /* make sure that stepsize value is always a power of 2 */
663 move_stepsize = (1 << log_2(move_stepsize));
665 return TILEX / move_stepsize;
668 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
671 int move_delay = get_move_delay_from_stepsize(move_stepsize);
672 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
674 /* do no immediately change move delay -- the player might just be moving */
675 player->move_delay_value_next = move_delay;
677 /* information if player can move must be set separately */
678 player->cannot_move = cannot_move;
682 player->move_delay = game.initial_move_delay;
683 player->move_delay_value = game.initial_move_delay_value;
685 player->move_delay_value_next = -1;
687 player->move_delay_reset_counter = 0;
691 void GetPlayerConfig()
693 if (!audio.sound_available)
694 setup.sound_simple = FALSE;
696 if (!audio.loops_available)
697 setup.sound_loops = FALSE;
699 if (!audio.music_available)
700 setup.sound_music = FALSE;
702 if (!video.fullscreen_available)
703 setup.fullscreen = FALSE;
705 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
707 SetAudioMode(setup.sound);
711 static int getBeltNrFromBeltElement(int element)
713 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
714 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
715 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
718 static int getBeltNrFromBeltActiveElement(int element)
720 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
721 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
722 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
725 static int getBeltNrFromBeltSwitchElement(int element)
727 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
728 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
729 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
732 static int getBeltDirNrFromBeltSwitchElement(int element)
734 static int belt_base_element[4] =
736 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
737 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
738 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
739 EL_CONVEYOR_BELT_4_SWITCH_LEFT
742 int belt_nr = getBeltNrFromBeltSwitchElement(element);
743 int belt_dir_nr = element - belt_base_element[belt_nr];
745 return (belt_dir_nr % 3);
748 static int getBeltDirFromBeltSwitchElement(int element)
750 static int belt_move_dir[3] =
757 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
759 return belt_move_dir[belt_dir_nr];
762 static void InitPlayerField(int x, int y, int element, boolean init_game)
764 if (element == EL_SP_MURPHY)
768 if (stored_player[0].present)
770 Feld[x][y] = EL_SP_MURPHY_CLONE;
776 stored_player[0].use_murphy = TRUE;
779 Feld[x][y] = EL_PLAYER_1;
785 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
786 int jx = player->jx, jy = player->jy;
788 player->present = TRUE;
790 player->block_last_field = (element == EL_SP_MURPHY ?
791 level.sp_block_last_field :
792 level.block_last_field);
794 /* ---------- initialize player's last field block delay --------------- */
796 /* always start with reliable default value (no adjustment needed) */
797 player->block_delay_adjustment = 0;
799 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
800 if (player->block_last_field && element == EL_SP_MURPHY)
801 player->block_delay_adjustment = 1;
803 /* special case 2: in game engines before 3.1.1, blocking was different */
804 if (game.use_block_last_field_bug)
805 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
807 if (!options.network || player->connected)
809 player->active = TRUE;
811 /* remove potentially duplicate players */
812 if (StorePlayer[jx][jy] == Feld[x][y])
813 StorePlayer[jx][jy] = 0;
815 StorePlayer[x][y] = Feld[x][y];
819 printf("Player %d activated.\n", player->element_nr);
820 printf("[Local player is %d and currently %s.]\n",
821 local_player->element_nr,
822 local_player->active ? "active" : "not active");
826 Feld[x][y] = EL_EMPTY;
828 player->jx = player->last_jx = x;
829 player->jy = player->last_jy = y;
833 static void InitField(int x, int y, boolean init_game)
835 int element = Feld[x][y];
844 InitPlayerField(x, y, element, init_game);
847 case EL_SOKOBAN_FIELD_PLAYER:
848 element = Feld[x][y] = EL_PLAYER_1;
849 InitField(x, y, init_game);
851 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
852 InitField(x, y, init_game);
855 case EL_SOKOBAN_FIELD_EMPTY:
856 local_player->sokobanfields_still_needed++;
860 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
861 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
862 else if (x > 0 && Feld[x-1][y] == EL_ACID)
863 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
864 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
865 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
866 else if (y > 0 && Feld[x][y-1] == EL_ACID)
867 Feld[x][y] = EL_ACID_POOL_BOTTOM;
868 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
869 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
877 case EL_SPACESHIP_RIGHT:
878 case EL_SPACESHIP_UP:
879 case EL_SPACESHIP_LEFT:
880 case EL_SPACESHIP_DOWN:
882 case EL_BD_BUTTERFLY_RIGHT:
883 case EL_BD_BUTTERFLY_UP:
884 case EL_BD_BUTTERFLY_LEFT:
885 case EL_BD_BUTTERFLY_DOWN:
886 case EL_BD_BUTTERFLY:
887 case EL_BD_FIREFLY_RIGHT:
888 case EL_BD_FIREFLY_UP:
889 case EL_BD_FIREFLY_LEFT:
890 case EL_BD_FIREFLY_DOWN:
892 case EL_PACMAN_RIGHT:
916 if (y == lev_fieldy - 1)
918 Feld[x][y] = EL_AMOEBA_GROWING;
919 Store[x][y] = EL_AMOEBA_WET;
923 case EL_DYNAMITE_ACTIVE:
924 case EL_SP_DISK_RED_ACTIVE:
925 case EL_DYNABOMB_PLAYER_1_ACTIVE:
926 case EL_DYNABOMB_PLAYER_2_ACTIVE:
927 case EL_DYNABOMB_PLAYER_3_ACTIVE:
928 case EL_DYNABOMB_PLAYER_4_ACTIVE:
933 local_player->lights_still_needed++;
937 local_player->friends_still_needed++;
942 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
945 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
946 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
947 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
948 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
949 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
950 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
951 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
952 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
953 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
954 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
955 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
956 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
959 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
960 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
961 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
963 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
965 game.belt_dir[belt_nr] = belt_dir;
966 game.belt_dir_nr[belt_nr] = belt_dir_nr;
968 else /* more than one switch -- set it like the first switch */
970 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
975 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
977 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
980 case EL_LIGHT_SWITCH_ACTIVE:
982 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
985 case EL_EMC_MAGIC_BALL:
987 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
990 case EL_EMC_MAGIC_BALL_SWITCH:
992 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
997 if (IS_CUSTOM_ELEMENT(element))
999 if (CAN_MOVE(element))
1002 #if USE_NEW_CUSTOM_VALUE
1003 if (!element_info[element].use_last_ce_value || init_game)
1004 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1008 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1011 else if (IS_GROUP_ELEMENT(element))
1013 struct ElementGroupInfo *group = element_info[element].group;
1014 int last_anim_random_frame = gfx.anim_random_frame;
1017 if (group->choice_mode == ANIM_RANDOM)
1018 gfx.anim_random_frame = RND(group->num_elements_resolved);
1020 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1021 group->choice_mode, 0,
1024 if (group->choice_mode == ANIM_RANDOM)
1025 gfx.anim_random_frame = last_anim_random_frame;
1027 group->choice_pos++;
1029 Feld[x][y] = group->element_resolved[element_pos];
1031 InitField(x, y, init_game);
1038 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1043 #if USE_NEW_CUSTOM_VALUE
1046 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1048 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1056 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1058 InitField(x, y, init_game);
1060 /* not needed to call InitMovDir() -- already done by InitField()! */
1061 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1062 CAN_MOVE(Feld[x][y]))
1066 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1068 int old_element = Feld[x][y];
1070 InitField(x, y, init_game);
1072 /* not needed to call InitMovDir() -- already done by InitField()! */
1073 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1074 CAN_MOVE(old_element) &&
1075 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1078 /* this case is in fact a combination of not less than three bugs:
1079 first, it calls InitMovDir() for elements that can move, although this is
1080 already done by InitField(); then, it checks the element that was at this
1081 field _before_ the call to InitField() (which can change it); lastly, it
1082 was not called for "mole with direction" elements, which were treated as
1083 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1087 inline void DrawGameValue_Emeralds(int value)
1089 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1092 inline void DrawGameValue_Dynamite(int value)
1094 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1097 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1101 /* currently only 4 of 8 possible keys are displayed */
1102 for (i = 0; i < STD_NUM_KEYS; i++)
1105 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1106 el2edimg(EL_KEY_1 + i));
1108 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1109 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1110 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1114 inline void DrawGameValue_Score(int value)
1116 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1119 inline void DrawGameValue_Time(int value)
1122 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1124 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1127 inline void DrawGameValue_Level(int value)
1130 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1133 /* misuse area for displaying emeralds to draw bigger level number */
1134 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1135 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1137 /* now copy it to the area for displaying level number */
1138 BlitBitmap(drawto, drawto,
1139 DX_EMERALDS, DY_EMERALDS + 1,
1140 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1141 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1142 DX_LEVEL - 1, DY_LEVEL + 1);
1144 /* restore the area for displaying emeralds */
1145 DrawGameValue_Emeralds(local_player->gems_still_needed);
1147 /* yes, this is all really ugly :-) */
1151 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1154 int key[MAX_NUM_KEYS];
1157 for (i = 0; i < MAX_NUM_KEYS; i++)
1158 key[i] = key_bits & (1 << i);
1160 DrawGameValue_Level(level_nr);
1162 DrawGameValue_Emeralds(emeralds);
1163 DrawGameValue_Dynamite(dynamite);
1164 DrawGameValue_Score(score);
1165 DrawGameValue_Time(time);
1167 DrawGameValue_Keys(key);
1170 void DrawGameDoorValues()
1174 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1176 DrawGameDoorValues_EM();
1181 DrawGameValue_Level(level_nr);
1183 DrawGameValue_Emeralds(local_player->gems_still_needed);
1184 DrawGameValue_Dynamite(local_player->inventory_size);
1185 DrawGameValue_Score(local_player->score);
1186 DrawGameValue_Time(TimeLeft);
1188 for (i = 0; i < MAX_PLAYERS; i++)
1189 DrawGameValue_Keys(stored_player[i].key);
1192 static void resolve_group_element(int group_element, int recursion_depth)
1194 static int group_nr;
1195 static struct ElementGroupInfo *group;
1196 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1199 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1201 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1202 group_element - EL_GROUP_START + 1);
1204 /* replace element which caused too deep recursion by question mark */
1205 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1210 if (recursion_depth == 0) /* initialization */
1212 group = element_info[group_element].group;
1213 group_nr = group_element - EL_GROUP_START;
1215 group->num_elements_resolved = 0;
1216 group->choice_pos = 0;
1219 for (i = 0; i < actual_group->num_elements; i++)
1221 int element = actual_group->element[i];
1223 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1226 if (IS_GROUP_ELEMENT(element))
1227 resolve_group_element(element, recursion_depth + 1);
1230 group->element_resolved[group->num_elements_resolved++] = element;
1231 element_info[element].in_group[group_nr] = TRUE;
1238 =============================================================================
1240 -----------------------------------------------------------------------------
1241 initialize game engine due to level / tape version number
1242 =============================================================================
1245 static void InitGameEngine()
1247 int i, j, k, l, x, y;
1249 /* set game engine from tape file when re-playing, else from level file */
1250 game.engine_version = (tape.playing ? tape.engine_version :
1251 level.game_version);
1253 /* ---------------------------------------------------------------------- */
1254 /* set flags for bugs and changes according to active game engine version */
1255 /* ---------------------------------------------------------------------- */
1258 Summary of bugfix/change:
1259 Fixed handling for custom elements that change when pushed by the player.
1261 Fixed/changed in version:
1265 Before 3.1.0, custom elements that "change when pushing" changed directly
1266 after the player started pushing them (until then handled in "DigField()").
1267 Since 3.1.0, these custom elements are not changed until the "pushing"
1268 move of the element is finished (now handled in "ContinueMoving()").
1270 Affected levels/tapes:
1271 The first condition is generally needed for all levels/tapes before version
1272 3.1.0, which might use the old behaviour before it was changed; known tapes
1273 that are affected are some tapes from the level set "Walpurgis Gardens" by
1275 The second condition is an exception from the above case and is needed for
1276 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1277 above (including some development versions of 3.1.0), but before it was
1278 known that this change would break tapes like the above and was fixed in
1279 3.1.1, so that the changed behaviour was active although the engine version
1280 while recording maybe was before 3.1.0. There is at least one tape that is
1281 affected by this exception, which is the tape for the one-level set "Bug
1282 Machine" by Juergen Bonhagen.
1285 game.use_change_when_pushing_bug =
1286 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1288 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1289 tape.game_version < VERSION_IDENT(3,1,1,0)));
1292 Summary of bugfix/change:
1293 Fixed handling for blocking the field the player leaves when moving.
1295 Fixed/changed in version:
1299 Before 3.1.1, when "block last field when moving" was enabled, the field
1300 the player is leaving when moving was blocked for the time of the move,
1301 and was directly unblocked afterwards. This resulted in the last field
1302 being blocked for exactly one less than the number of frames of one player
1303 move. Additionally, even when blocking was disabled, the last field was
1304 blocked for exactly one frame.
1305 Since 3.1.1, due to changes in player movement handling, the last field
1306 is not blocked at all when blocking is disabled. When blocking is enabled,
1307 the last field is blocked for exactly the number of frames of one player
1308 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1309 last field is blocked for exactly one more than the number of frames of
1312 Affected levels/tapes:
1313 (!!! yet to be determined -- probably many !!!)
1316 game.use_block_last_field_bug =
1317 (game.engine_version < VERSION_IDENT(3,1,1,0));
1320 Summary of bugfix/change:
1321 Changed behaviour of CE changes with multiple changes per single frame.
1323 Fixed/changed in version:
1327 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1328 This resulted in race conditions where CEs seem to behave strange in some
1329 situations (where triggered CE changes were just skipped because there was
1330 already a CE change on that tile in the playfield in that engine frame).
1331 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1332 (The number of changes per frame must be limited in any case, because else
1333 it is easily possible to define CE changes that would result in an infinite
1334 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1335 should be set large enough so that it would only be reached in cases where
1336 the corresponding CE change conditions run into a loop. Therefore, it seems
1337 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1338 maximal number of change pages for custom elements.)
1340 Affected levels/tapes:
1344 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1345 game.max_num_changes_per_frame = 1;
1347 game.max_num_changes_per_frame =
1348 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1351 /* ---------------------------------------------------------------------- */
1353 /* default scan direction: scan playfield from top/left to bottom/right */
1354 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1356 /* dynamically adjust element properties according to game engine version */
1357 InitElementPropertiesEngine(game.engine_version);
1360 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1361 printf(" tape version == %06d [%s] [file: %06d]\n",
1362 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1364 printf(" => game.engine_version == %06d\n", game.engine_version);
1367 /* ---------- recursively resolve group elements ------------------------- */
1369 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1370 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1371 element_info[i].in_group[j] = FALSE;
1373 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1374 resolve_group_element(EL_GROUP_START + i, 0);
1376 /* ---------- initialize player's initial move delay --------------------- */
1379 /* dynamically adjust player properties according to level information */
1380 game.initial_move_delay_value =
1381 get_move_delay_from_stepsize(level.initial_player_stepsize);
1383 /* dynamically adjust player properties according to level information */
1384 game.initial_move_delay_value =
1385 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1388 /* dynamically adjust player properties according to game engine version */
1389 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1390 game.initial_move_delay_value : 0);
1392 /* ---------- initialize player's initial push delay --------------------- */
1394 /* dynamically adjust player properties according to game engine version */
1395 game.initial_push_delay_value =
1396 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1398 /* ---------- initialize changing elements ------------------------------- */
1400 /* initialize changing elements information */
1401 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1403 struct ElementInfo *ei = &element_info[i];
1405 /* this pointer might have been changed in the level editor */
1406 ei->change = &ei->change_page[0];
1408 if (!IS_CUSTOM_ELEMENT(i))
1410 ei->change->target_element = EL_EMPTY_SPACE;
1411 ei->change->delay_fixed = 0;
1412 ei->change->delay_random = 0;
1413 ei->change->delay_frames = 1;
1416 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1418 ei->has_change_event[j] = FALSE;
1420 ei->event_page_nr[j] = 0;
1421 ei->event_page[j] = &ei->change_page[0];
1425 /* add changing elements from pre-defined list */
1426 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1428 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1429 struct ElementInfo *ei = &element_info[ch_delay->element];
1431 ei->change->target_element = ch_delay->target_element;
1432 ei->change->delay_fixed = ch_delay->change_delay;
1434 ei->change->pre_change_function = ch_delay->pre_change_function;
1435 ei->change->change_function = ch_delay->change_function;
1436 ei->change->post_change_function = ch_delay->post_change_function;
1438 ei->change->can_change = TRUE;
1439 ei->change->can_change_or_has_action = TRUE;
1441 ei->has_change_event[CE_DELAY] = TRUE;
1443 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1444 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1447 /* ---------- initialize internal run-time variables ------------- */
1449 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1451 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1453 for (j = 0; j < ei->num_change_pages; j++)
1455 ei->change_page[j].can_change_or_has_action =
1456 (ei->change_page[j].can_change |
1457 ei->change_page[j].has_action);
1461 /* add change events from custom element configuration */
1462 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1464 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1466 for (j = 0; j < ei->num_change_pages; j++)
1468 if (!ei->change_page[j].can_change_or_has_action)
1471 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1473 /* only add event page for the first page found with this event */
1474 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1476 ei->has_change_event[k] = TRUE;
1478 ei->event_page_nr[k] = j;
1479 ei->event_page[k] = &ei->change_page[j];
1485 /* ---------- initialize run-time trigger player and element ------------- */
1487 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1489 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1491 for (j = 0; j < ei->num_change_pages; j++)
1493 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1494 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1495 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1496 ei->change_page[j].actual_trigger_ce_value = 0;
1500 /* ---------- initialize trigger events ---------------------------------- */
1502 /* initialize trigger events information */
1503 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1504 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1505 trigger_events[i][j] = FALSE;
1507 /* add trigger events from element change event properties */
1508 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1510 struct ElementInfo *ei = &element_info[i];
1512 for (j = 0; j < ei->num_change_pages; j++)
1514 if (!ei->change_page[j].can_change_or_has_action)
1517 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1519 int trigger_element = ei->change_page[j].trigger_element;
1521 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1523 if (ei->change_page[j].has_event[k])
1525 if (IS_GROUP_ELEMENT(trigger_element))
1527 struct ElementGroupInfo *group =
1528 element_info[trigger_element].group;
1530 for (l = 0; l < group->num_elements_resolved; l++)
1531 trigger_events[group->element_resolved[l]][k] = TRUE;
1534 trigger_events[trigger_element][k] = TRUE;
1541 /* ---------- initialize push delay -------------------------------------- */
1543 /* initialize push delay values to default */
1544 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1546 if (!IS_CUSTOM_ELEMENT(i))
1548 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1549 element_info[i].push_delay_random = game.default_push_delay_random;
1553 /* set push delay value for certain elements from pre-defined list */
1554 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1556 int e = push_delay_list[i].element;
1558 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1559 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1562 /* set push delay value for Supaplex elements for newer engine versions */
1563 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1565 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1567 if (IS_SP_ELEMENT(i))
1569 /* set SP push delay to just enough to push under a falling zonk */
1570 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1572 element_info[i].push_delay_fixed = delay;
1573 element_info[i].push_delay_random = 0;
1578 /* ---------- initialize move stepsize ----------------------------------- */
1580 /* initialize move stepsize values to default */
1581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1582 if (!IS_CUSTOM_ELEMENT(i))
1583 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1585 /* set move stepsize value for certain elements from pre-defined list */
1586 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1588 int e = move_stepsize_list[i].element;
1590 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1593 /* ---------- initialize collect score ----------------------------------- */
1595 /* initialize collect score values for custom elements from initial value */
1596 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1597 if (IS_CUSTOM_ELEMENT(i))
1598 element_info[i].collect_score = element_info[i].collect_score_initial;
1600 /* ---------- initialize collect count ----------------------------------- */
1602 /* initialize collect count values for non-custom elements */
1603 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1604 if (!IS_CUSTOM_ELEMENT(i))
1605 element_info[i].collect_count_initial = 0;
1607 /* add collect count values for all elements from pre-defined list */
1608 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1609 element_info[collect_count_list[i].element].collect_count_initial =
1610 collect_count_list[i].count;
1612 /* ---------- initialize access direction -------------------------------- */
1614 /* initialize access direction values to default (access from every side) */
1615 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1616 if (!IS_CUSTOM_ELEMENT(i))
1617 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1619 /* set access direction value for certain elements from pre-defined list */
1620 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1621 element_info[access_direction_list[i].element].access_direction =
1622 access_direction_list[i].direction;
1624 /* ---------- initialize explosion content ------------------------------- */
1625 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1627 if (IS_CUSTOM_ELEMENT(i))
1630 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1632 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1634 element_info[i].content.e[x][y] =
1635 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1636 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1637 i == EL_PLAYER_3 ? EL_EMERALD :
1638 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1639 i == EL_MOLE ? EL_EMERALD_RED :
1640 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1641 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1642 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1643 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1644 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1645 i == EL_WALL_EMERALD ? EL_EMERALD :
1646 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1647 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1648 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1649 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1650 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1651 i == EL_WALL_PEARL ? EL_PEARL :
1652 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1658 int get_num_special_action(int element, int action_first, int action_last)
1660 int num_special_action = 0;
1663 for (i = action_first; i <= action_last; i++)
1665 boolean found = FALSE;
1667 for (j = 0; j < NUM_DIRECTIONS; j++)
1668 if (el_act_dir2img(element, i, j) !=
1669 el_act_dir2img(element, ACTION_DEFAULT, j))
1673 num_special_action++;
1678 return num_special_action;
1682 =============================================================================
1684 -----------------------------------------------------------------------------
1685 initialize and start new game
1686 =============================================================================
1691 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1692 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1693 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1698 /* don't play tapes over network */
1699 network_playing = (options.network && !tape.playing);
1701 for (i = 0; i < MAX_PLAYERS; i++)
1703 struct PlayerInfo *player = &stored_player[i];
1705 player->index_nr = i;
1706 player->index_bit = (1 << i);
1707 player->element_nr = EL_PLAYER_1 + i;
1709 player->present = FALSE;
1710 player->active = FALSE;
1713 player->effective_action = 0;
1714 player->programmed_action = 0;
1717 player->gems_still_needed = level.gems_needed;
1718 player->sokobanfields_still_needed = 0;
1719 player->lights_still_needed = 0;
1720 player->friends_still_needed = 0;
1722 for (j = 0; j < MAX_NUM_KEYS; j++)
1723 player->key[j] = FALSE;
1725 player->dynabomb_count = 0;
1726 player->dynabomb_size = 1;
1727 player->dynabombs_left = 0;
1728 player->dynabomb_xl = FALSE;
1730 player->MovDir = MV_NONE;
1733 player->GfxDir = MV_NONE;
1734 player->GfxAction = ACTION_DEFAULT;
1736 player->StepFrame = 0;
1738 player->use_murphy = FALSE;
1739 player->artwork_element =
1740 (level.use_artwork_element[i] ? level.artwork_element[i] :
1741 player->element_nr);
1743 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1744 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1746 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1748 player->actual_frame_counter = 0;
1750 player->step_counter = 0;
1752 player->last_move_dir = MV_NONE;
1754 player->is_waiting = FALSE;
1755 player->is_moving = FALSE;
1756 player->is_auto_moving = FALSE;
1757 player->is_digging = FALSE;
1758 player->is_snapping = FALSE;
1759 player->is_collecting = FALSE;
1760 player->is_pushing = FALSE;
1761 player->is_switching = FALSE;
1762 player->is_dropping = FALSE;
1764 player->is_bored = FALSE;
1765 player->is_sleeping = FALSE;
1767 player->frame_counter_bored = -1;
1768 player->frame_counter_sleeping = -1;
1770 player->anim_delay_counter = 0;
1771 player->post_delay_counter = 0;
1773 player->action_waiting = ACTION_DEFAULT;
1774 player->last_action_waiting = ACTION_DEFAULT;
1775 player->special_action_bored = ACTION_DEFAULT;
1776 player->special_action_sleeping = ACTION_DEFAULT;
1778 /* set number of special actions for bored and sleeping animation */
1779 player->num_special_action_bored =
1780 get_num_special_action(player->artwork_element,
1781 ACTION_BORING_1, ACTION_BORING_LAST);
1782 player->num_special_action_sleeping =
1783 get_num_special_action(player->artwork_element,
1784 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1786 player->switch_x = -1;
1787 player->switch_y = -1;
1789 player->drop_x = -1;
1790 player->drop_y = -1;
1792 player->show_envelope = 0;
1795 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1797 player->move_delay = game.initial_move_delay;
1798 player->move_delay_value = game.initial_move_delay_value;
1800 player->move_delay_value_next = -1;
1802 player->move_delay_reset_counter = 0;
1804 player->cannot_move = FALSE;
1807 player->push_delay = -1; /* initialized when pushing starts */
1808 player->push_delay_value = game.initial_push_delay_value;
1810 player->drop_delay = 0;
1812 player->last_jx = player->last_jy = 0;
1813 player->jx = player->jy = 0;
1815 player->shield_normal_time_left = 0;
1816 player->shield_deadly_time_left = 0;
1818 player->inventory_infinite_element = EL_UNDEFINED;
1819 player->inventory_size = 0;
1821 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1822 SnapField(player, 0, 0);
1824 player->LevelSolved = FALSE;
1825 player->GameOver = FALSE;
1828 network_player_action_received = FALSE;
1830 #if defined(NETWORK_AVALIABLE)
1831 /* initial null action */
1832 if (network_playing)
1833 SendToServer_MovePlayer(MV_NONE);
1842 TimeLeft = level.time;
1845 ScreenMovDir = MV_NONE;
1849 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1851 AllPlayersGone = FALSE;
1853 game.yamyam_content_nr = 0;
1854 game.magic_wall_active = FALSE;
1855 game.magic_wall_time_left = 0;
1856 game.light_time_left = 0;
1857 game.timegate_time_left = 0;
1858 game.switchgate_pos = 0;
1859 game.wind_direction = level.wind_direction_initial;
1860 game.gravity = level.initial_gravity;
1861 game.explosions_delayed = TRUE;
1863 game.lenses_time_left = 0;
1864 game.magnify_time_left = 0;
1866 game.ball_state = level.ball_state_initial;
1867 game.ball_content_nr = 0;
1869 game.envelope_active = FALSE;
1871 for (i = 0; i < NUM_BELTS; i++)
1873 game.belt_dir[i] = MV_NONE;
1874 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1877 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1878 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1881 SCAN_PLAYFIELD(x, y)
1883 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1886 Feld[x][y] = level.field[x][y];
1887 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1888 ChangeDelay[x][y] = 0;
1889 ChangePage[x][y] = -1;
1890 #if USE_NEW_CUSTOM_VALUE
1891 CustomValue[x][y] = 0; /* initialized in InitField() */
1893 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1895 WasJustMoving[x][y] = 0;
1896 WasJustFalling[x][y] = 0;
1897 CheckCollision[x][y] = 0;
1899 Pushed[x][y] = FALSE;
1901 ChangeCount[x][y] = 0;
1902 ChangeEvent[x][y] = -1;
1904 ExplodePhase[x][y] = 0;
1905 ExplodeDelay[x][y] = 0;
1906 ExplodeField[x][y] = EX_TYPE_NONE;
1908 RunnerVisit[x][y] = 0;
1909 PlayerVisit[x][y] = 0;
1912 GfxRandom[x][y] = INIT_GFX_RANDOM();
1913 GfxElement[x][y] = EL_UNDEFINED;
1914 GfxAction[x][y] = ACTION_DEFAULT;
1915 GfxDir[x][y] = MV_NONE;
1919 SCAN_PLAYFIELD(x, y)
1921 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1924 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1926 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1928 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1931 InitField(x, y, TRUE);
1936 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1937 emulate_sb ? EMU_SOKOBAN :
1938 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1940 #if USE_NEW_ALL_SLIPPERY
1941 /* initialize type of slippery elements */
1942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1944 if (!IS_CUSTOM_ELEMENT(i))
1946 /* default: elements slip down either to the left or right randomly */
1947 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1949 /* SP style elements prefer to slip down on the left side */
1950 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1951 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1953 /* BD style elements prefer to slip down on the left side */
1954 if (game.emulation == EMU_BOULDERDASH)
1955 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1960 /* initialize explosion and ignition delay */
1961 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1963 if (!IS_CUSTOM_ELEMENT(i))
1966 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1967 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1968 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1969 int last_phase = (num_phase + 1) * delay;
1970 int half_phase = (num_phase / 2) * delay;
1972 element_info[i].explosion_delay = last_phase - 1;
1973 element_info[i].ignition_delay = half_phase;
1975 if (i == EL_BLACK_ORB)
1976 element_info[i].ignition_delay = 1;
1980 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1981 element_info[i].explosion_delay = 1;
1983 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1984 element_info[i].ignition_delay = 1;
1988 /* correct non-moving belts to start moving left */
1989 for (i = 0; i < NUM_BELTS; i++)
1990 if (game.belt_dir[i] == MV_NONE)
1991 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1993 /* check if any connected player was not found in playfield */
1994 for (i = 0; i < MAX_PLAYERS; i++)
1996 struct PlayerInfo *player = &stored_player[i];
1998 if (player->connected && !player->present)
2000 for (j = 0; j < MAX_PLAYERS; j++)
2002 struct PlayerInfo *some_player = &stored_player[j];
2003 int jx = some_player->jx, jy = some_player->jy;
2005 /* assign first free player found that is present in the playfield */
2006 if (some_player->present && !some_player->connected)
2008 player->present = TRUE;
2009 player->active = TRUE;
2011 some_player->present = FALSE;
2012 some_player->active = FALSE;
2015 player->element_nr = some_player->element_nr;
2018 player->artwork_element = some_player->artwork_element;
2020 player->block_last_field = some_player->block_last_field;
2021 player->block_delay_adjustment = some_player->block_delay_adjustment;
2023 StorePlayer[jx][jy] = player->element_nr;
2024 player->jx = player->last_jx = jx;
2025 player->jy = player->last_jy = jy;
2035 /* when playing a tape, eliminate all players which do not participate */
2037 for (i = 0; i < MAX_PLAYERS; i++)
2039 if (stored_player[i].active && !tape.player_participates[i])
2041 struct PlayerInfo *player = &stored_player[i];
2042 int jx = player->jx, jy = player->jy;
2044 player->active = FALSE;
2045 StorePlayer[jx][jy] = 0;
2046 Feld[jx][jy] = EL_EMPTY;
2050 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2052 /* when in single player mode, eliminate all but the first active player */
2054 for (i = 0; i < MAX_PLAYERS; i++)
2056 if (stored_player[i].active)
2058 for (j = i + 1; j < MAX_PLAYERS; j++)
2060 if (stored_player[j].active)
2062 struct PlayerInfo *player = &stored_player[j];
2063 int jx = player->jx, jy = player->jy;
2065 player->active = FALSE;
2066 player->present = FALSE;
2068 StorePlayer[jx][jy] = 0;
2069 Feld[jx][jy] = EL_EMPTY;
2076 /* when recording the game, store which players take part in the game */
2079 for (i = 0; i < MAX_PLAYERS; i++)
2080 if (stored_player[i].active)
2081 tape.player_participates[i] = TRUE;
2086 for (i = 0; i < MAX_PLAYERS; i++)
2088 struct PlayerInfo *player = &stored_player[i];
2090 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2095 if (local_player == player)
2096 printf("Player %d is local player.\n", i+1);
2100 if (BorderElement == EL_EMPTY)
2103 SBX_Right = lev_fieldx - SCR_FIELDX;
2105 SBY_Lower = lev_fieldy - SCR_FIELDY;
2110 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2112 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2115 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2116 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2118 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2119 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2121 /* if local player not found, look for custom element that might create
2122 the player (make some assumptions about the right custom element) */
2123 if (!local_player->present)
2125 int start_x = 0, start_y = 0;
2126 int found_rating = 0;
2127 int found_element = EL_UNDEFINED;
2128 int player_nr = local_player->index_nr;
2131 SCAN_PLAYFIELD(x, y)
2133 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2136 int element = Feld[x][y];
2141 if (level.use_start_element[player_nr] &&
2142 level.start_element[player_nr] == element &&
2149 found_element = element;
2152 if (!IS_CUSTOM_ELEMENT(element))
2155 if (CAN_CHANGE(element))
2157 for (i = 0; i < element_info[element].num_change_pages; i++)
2159 /* check for player created from custom element as single target */
2160 content = element_info[element].change_page[i].target_element;
2161 is_player = ELEM_IS_PLAYER(content);
2163 if (is_player && (found_rating < 3 || element < found_element))
2169 found_element = element;
2174 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2176 /* check for player created from custom element as explosion content */
2177 content = element_info[element].content.e[xx][yy];
2178 is_player = ELEM_IS_PLAYER(content);
2180 if (is_player && (found_rating < 2 || element < found_element))
2182 start_x = x + xx - 1;
2183 start_y = y + yy - 1;
2186 found_element = element;
2189 if (!CAN_CHANGE(element))
2192 for (i = 0; i < element_info[element].num_change_pages; i++)
2194 /* check for player created from custom element as extended target */
2196 element_info[element].change_page[i].target_content.e[xx][yy];
2198 is_player = ELEM_IS_PLAYER(content);
2200 if (is_player && (found_rating < 1 || element < found_element))
2202 start_x = x + xx - 1;
2203 start_y = y + yy - 1;
2206 found_element = element;
2212 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2213 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2216 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2217 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2222 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2223 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2224 local_player->jx - MIDPOSX);
2226 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2227 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2228 local_player->jy - MIDPOSY);
2231 if (!game.restart_level)
2232 CloseDoor(DOOR_CLOSE_1);
2234 /* !!! FIX THIS (START) !!! */
2235 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2237 InitGameEngine_EM();
2244 /* after drawing the level, correct some elements */
2245 if (game.timegate_time_left == 0)
2246 CloseAllOpenTimegates();
2248 if (setup.soft_scrolling)
2249 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2251 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2254 /* !!! FIX THIS (END) !!! */
2256 if (!game.restart_level)
2258 /* copy default game door content to main double buffer */
2259 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2260 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2263 DrawGameDoorValues();
2265 if (!game.restart_level)
2269 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2270 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2271 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2275 /* copy actual game door content to door double buffer for OpenDoor() */
2276 BlitBitmap(drawto, bitmap_db_door,
2277 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2279 OpenDoor(DOOR_OPEN_ALL);
2281 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2283 if (setup.sound_music)
2286 KeyboardAutoRepeatOffUnlessAutoplay();
2290 for (i = 0; i < MAX_PLAYERS; i++)
2291 printf("Player %d %sactive.\n",
2292 i + 1, (stored_player[i].active ? "" : "not "));
2296 game.restart_level = FALSE;
2299 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2301 /* this is used for non-R'n'D game engines to update certain engine values */
2303 /* needed to determine if sounds are played within the visible screen area */
2304 scroll_x = actual_scroll_x;
2305 scroll_y = actual_scroll_y;
2308 void InitMovDir(int x, int y)
2310 int i, element = Feld[x][y];
2311 static int xy[4][2] =
2318 static int direction[3][4] =
2320 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2321 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2322 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2331 Feld[x][y] = EL_BUG;
2332 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2335 case EL_SPACESHIP_RIGHT:
2336 case EL_SPACESHIP_UP:
2337 case EL_SPACESHIP_LEFT:
2338 case EL_SPACESHIP_DOWN:
2339 Feld[x][y] = EL_SPACESHIP;
2340 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2343 case EL_BD_BUTTERFLY_RIGHT:
2344 case EL_BD_BUTTERFLY_UP:
2345 case EL_BD_BUTTERFLY_LEFT:
2346 case EL_BD_BUTTERFLY_DOWN:
2347 Feld[x][y] = EL_BD_BUTTERFLY;
2348 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2351 case EL_BD_FIREFLY_RIGHT:
2352 case EL_BD_FIREFLY_UP:
2353 case EL_BD_FIREFLY_LEFT:
2354 case EL_BD_FIREFLY_DOWN:
2355 Feld[x][y] = EL_BD_FIREFLY;
2356 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2359 case EL_PACMAN_RIGHT:
2361 case EL_PACMAN_LEFT:
2362 case EL_PACMAN_DOWN:
2363 Feld[x][y] = EL_PACMAN;
2364 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2367 case EL_SP_SNIKSNAK:
2368 MovDir[x][y] = MV_UP;
2371 case EL_SP_ELECTRON:
2372 MovDir[x][y] = MV_LEFT;
2379 Feld[x][y] = EL_MOLE;
2380 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2384 if (IS_CUSTOM_ELEMENT(element))
2386 struct ElementInfo *ei = &element_info[element];
2387 int move_direction_initial = ei->move_direction_initial;
2388 int move_pattern = ei->move_pattern;
2390 if (move_direction_initial == MV_START_PREVIOUS)
2392 if (MovDir[x][y] != MV_NONE)
2395 move_direction_initial = MV_START_AUTOMATIC;
2398 if (move_direction_initial == MV_START_RANDOM)
2399 MovDir[x][y] = 1 << RND(4);
2400 else if (move_direction_initial & MV_ANY_DIRECTION)
2401 MovDir[x][y] = move_direction_initial;
2402 else if (move_pattern == MV_ALL_DIRECTIONS ||
2403 move_pattern == MV_TURNING_LEFT ||
2404 move_pattern == MV_TURNING_RIGHT ||
2405 move_pattern == MV_TURNING_LEFT_RIGHT ||
2406 move_pattern == MV_TURNING_RIGHT_LEFT ||
2407 move_pattern == MV_TURNING_RANDOM)
2408 MovDir[x][y] = 1 << RND(4);
2409 else if (move_pattern == MV_HORIZONTAL)
2410 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2411 else if (move_pattern == MV_VERTICAL)
2412 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2413 else if (move_pattern & MV_ANY_DIRECTION)
2414 MovDir[x][y] = element_info[element].move_pattern;
2415 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2416 move_pattern == MV_ALONG_RIGHT_SIDE)
2418 /* use random direction as default start direction */
2419 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2420 MovDir[x][y] = 1 << RND(4);
2422 for (i = 0; i < NUM_DIRECTIONS; i++)
2424 int x1 = x + xy[i][0];
2425 int y1 = y + xy[i][1];
2427 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2429 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2430 MovDir[x][y] = direction[0][i];
2432 MovDir[x][y] = direction[1][i];
2441 MovDir[x][y] = 1 << RND(4);
2443 if (element != EL_BUG &&
2444 element != EL_SPACESHIP &&
2445 element != EL_BD_BUTTERFLY &&
2446 element != EL_BD_FIREFLY)
2449 for (i = 0; i < NUM_DIRECTIONS; i++)
2451 int x1 = x + xy[i][0];
2452 int y1 = y + xy[i][1];
2454 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2456 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2458 MovDir[x][y] = direction[0][i];
2461 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2462 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2464 MovDir[x][y] = direction[1][i];
2473 GfxDir[x][y] = MovDir[x][y];
2476 void InitAmoebaNr(int x, int y)
2479 int group_nr = AmoebeNachbarNr(x, y);
2483 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2485 if (AmoebaCnt[i] == 0)
2493 AmoebaNr[x][y] = group_nr;
2494 AmoebaCnt[group_nr]++;
2495 AmoebaCnt2[group_nr]++;
2501 boolean raise_level = FALSE;
2503 if (local_player->MovPos)
2506 if (tape.auto_play) /* tape might already be stopped here */
2507 tape.auto_play_level_solved = TRUE;
2509 local_player->LevelSolved = FALSE;
2511 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2515 if (!tape.playing && setup.sound_loops)
2516 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2517 SND_CTRL_PLAY_LOOP);
2519 while (TimeLeft > 0)
2521 if (!tape.playing && !setup.sound_loops)
2522 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2524 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2527 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2532 RaiseScore(level.score[SC_TIME_BONUS]);
2535 DrawGameValue_Time(TimeLeft);
2543 if (!tape.playing && setup.sound_loops)
2544 StopSound(SND_GAME_LEVELTIME_BONUS);
2546 else if (level.time == 0) /* level without time limit */
2548 if (!tape.playing && setup.sound_loops)
2549 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2550 SND_CTRL_PLAY_LOOP);
2552 while (TimePlayed < 999)
2554 if (!tape.playing && !setup.sound_loops)
2555 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2557 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2560 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2565 RaiseScore(level.score[SC_TIME_BONUS]);
2568 DrawGameValue_Time(TimePlayed);
2576 if (!tape.playing && setup.sound_loops)
2577 StopSound(SND_GAME_LEVELTIME_BONUS);
2580 /* close exit door after last player */
2581 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2582 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2583 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2585 int element = Feld[ExitX][ExitY];
2587 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2588 EL_SP_EXIT_CLOSING);
2590 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2593 /* player disappears */
2594 if (ExitX >= 0 && ExitY >= 0)
2595 DrawLevelField(ExitX, ExitY);
2602 CloseDoor(DOOR_CLOSE_1);
2607 SaveTape(tape.level_nr); /* Ask to save tape */
2610 if (level_nr == leveldir_current->handicap_level)
2612 leveldir_current->handicap_level++;
2613 SaveLevelSetup_SeriesInfo();
2616 if (level_editor_test_game)
2617 local_player->score = -1; /* no highscore when playing from editor */
2618 else if (level_nr < leveldir_current->last_level)
2619 raise_level = TRUE; /* advance to next level */
2621 if ((hi_pos = NewHiScore()) >= 0)
2623 game_status = GAME_MODE_SCORES;
2624 DrawHallOfFame(hi_pos);
2633 game_status = GAME_MODE_MAIN;
2650 LoadScore(level_nr);
2652 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2653 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2656 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2658 if (local_player->score > highscore[k].Score)
2660 /* player has made it to the hall of fame */
2662 if (k < MAX_SCORE_ENTRIES - 1)
2664 int m = MAX_SCORE_ENTRIES - 1;
2667 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2668 if (!strcmp(setup.player_name, highscore[l].Name))
2670 if (m == k) /* player's new highscore overwrites his old one */
2674 for (l = m; l > k; l--)
2676 strcpy(highscore[l].Name, highscore[l - 1].Name);
2677 highscore[l].Score = highscore[l - 1].Score;
2684 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2685 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2686 highscore[k].Score = local_player->score;
2692 else if (!strncmp(setup.player_name, highscore[k].Name,
2693 MAX_PLAYER_NAME_LEN))
2694 break; /* player already there with a higher score */
2700 SaveScore(level_nr);
2705 inline static int getElementMoveStepsize(int x, int y)
2707 int element = Feld[x][y];
2708 int direction = MovDir[x][y];
2709 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2710 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2711 int horiz_move = (dx != 0);
2712 int sign = (horiz_move ? dx : dy);
2713 int step = sign * element_info[element].move_stepsize;
2715 /* special values for move stepsize for spring and things on conveyor belt */
2719 if (element == EL_SPRING)
2720 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2721 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2722 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2723 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2725 if (CAN_FALL(element) &&
2726 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2727 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2728 else if (element == EL_SPRING)
2729 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2736 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2738 if (player->GfxAction != action || player->GfxDir != dir)
2741 printf("Player frame reset! (%d => %d, %d => %d)\n",
2742 player->GfxAction, action, player->GfxDir, dir);
2745 player->GfxAction = action;
2746 player->GfxDir = dir;
2748 player->StepFrame = 0;
2752 static void ResetRandomAnimationValue(int x, int y)
2754 GfxRandom[x][y] = INIT_GFX_RANDOM();
2757 static void ResetGfxAnimation(int x, int y)
2760 GfxAction[x][y] = ACTION_DEFAULT;
2761 GfxDir[x][y] = MovDir[x][y];
2764 void InitMovingField(int x, int y, int direction)
2766 int element = Feld[x][y];
2767 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2768 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2772 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2773 ResetGfxAnimation(x, y);
2775 MovDir[x][y] = direction;
2776 GfxDir[x][y] = direction;
2777 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2778 ACTION_FALLING : ACTION_MOVING);
2780 /* this is needed for CEs with property "can move" / "not moving" */
2782 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2784 if (Feld[newx][newy] == EL_EMPTY)
2785 Feld[newx][newy] = EL_BLOCKED;
2787 MovDir[newx][newy] = MovDir[x][y];
2789 #if USE_NEW_CUSTOM_VALUE
2790 CustomValue[newx][newy] = CustomValue[x][y];
2793 GfxFrame[newx][newy] = GfxFrame[x][y];
2794 GfxRandom[newx][newy] = GfxRandom[x][y];
2795 GfxAction[newx][newy] = GfxAction[x][y];
2796 GfxDir[newx][newy] = GfxDir[x][y];
2800 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2802 int direction = MovDir[x][y];
2803 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2804 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2810 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2812 int oldx = x, oldy = y;
2813 int direction = MovDir[x][y];
2815 if (direction == MV_LEFT)
2817 else if (direction == MV_RIGHT)
2819 else if (direction == MV_UP)
2821 else if (direction == MV_DOWN)
2824 *comes_from_x = oldx;
2825 *comes_from_y = oldy;
2828 int MovingOrBlocked2Element(int x, int y)
2830 int element = Feld[x][y];
2832 if (element == EL_BLOCKED)
2836 Blocked2Moving(x, y, &oldx, &oldy);
2837 return Feld[oldx][oldy];
2843 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2845 /* like MovingOrBlocked2Element(), but if element is moving
2846 and (x,y) is the field the moving element is just leaving,
2847 return EL_BLOCKED instead of the element value */
2848 int element = Feld[x][y];
2850 if (IS_MOVING(x, y))
2852 if (element == EL_BLOCKED)
2856 Blocked2Moving(x, y, &oldx, &oldy);
2857 return Feld[oldx][oldy];
2866 static void RemoveField(int x, int y)
2868 Feld[x][y] = EL_EMPTY;
2874 #if USE_NEW_CUSTOM_VALUE
2875 CustomValue[x][y] = 0;
2879 ChangeDelay[x][y] = 0;
2880 ChangePage[x][y] = -1;
2881 Pushed[x][y] = FALSE;
2884 ExplodeField[x][y] = EX_TYPE_NONE;
2887 GfxElement[x][y] = EL_UNDEFINED;
2888 GfxAction[x][y] = ACTION_DEFAULT;
2889 GfxDir[x][y] = MV_NONE;
2892 void RemoveMovingField(int x, int y)
2894 int oldx = x, oldy = y, newx = x, newy = y;
2895 int element = Feld[x][y];
2896 int next_element = EL_UNDEFINED;
2898 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2901 if (IS_MOVING(x, y))
2903 Moving2Blocked(x, y, &newx, &newy);
2905 if (Feld[newx][newy] != EL_BLOCKED)
2907 /* element is moving, but target field is not free (blocked), but
2908 already occupied by something different (example: acid pool);
2909 in this case, only remove the moving field, but not the target */
2911 RemoveField(oldx, oldy);
2913 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2915 DrawLevelField(oldx, oldy);
2920 else if (element == EL_BLOCKED)
2922 Blocked2Moving(x, y, &oldx, &oldy);
2923 if (!IS_MOVING(oldx, oldy))
2927 if (element == EL_BLOCKED &&
2928 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2929 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2930 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2931 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2932 next_element = get_next_element(Feld[oldx][oldy]);
2934 RemoveField(oldx, oldy);
2935 RemoveField(newx, newy);
2937 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2939 if (next_element != EL_UNDEFINED)
2940 Feld[oldx][oldy] = next_element;
2942 DrawLevelField(oldx, oldy);
2943 DrawLevelField(newx, newy);
2946 void DrawDynamite(int x, int y)
2948 int sx = SCREENX(x), sy = SCREENY(y);
2949 int graphic = el2img(Feld[x][y]);
2952 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2955 if (IS_WALKABLE_INSIDE(Back[x][y]))
2959 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2960 else if (Store[x][y])
2961 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2963 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2965 if (Back[x][y] || Store[x][y])
2966 DrawGraphicThruMask(sx, sy, graphic, frame);
2968 DrawGraphic(sx, sy, graphic, frame);
2971 void CheckDynamite(int x, int y)
2973 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2977 if (MovDelay[x][y] != 0)
2980 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2986 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2991 void DrawRelocatePlayer(struct PlayerInfo *player)
2993 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2994 boolean no_delay = (tape.warp_forward);
2995 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2996 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2997 int jx = player->jx;
2998 int jy = player->jy;
3000 if (level.instant_relocation)
3002 int offset = (setup.scroll_delay ? 3 : 0);
3004 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3006 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3007 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3008 local_player->jx - MIDPOSX);
3010 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3011 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3012 local_player->jy - MIDPOSY);
3016 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3017 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3018 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3020 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3021 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3022 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3024 /* don't scroll over playfield boundaries */
3025 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3026 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3028 /* don't scroll over playfield boundaries */
3029 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3030 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3033 RedrawPlayfield(TRUE, 0,0,0,0);
3037 int scroll_xx = -999, scroll_yy = -999;
3039 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3041 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3044 int fx = FX, fy = FY;
3046 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3047 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3048 local_player->jx - MIDPOSX);
3050 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3051 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3052 local_player->jy - MIDPOSY);
3054 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3055 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3057 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3063 fx += dx * TILEX / 2;
3064 fy += dy * TILEY / 2;
3066 ScrollLevel(dx, dy);
3069 /* scroll in two steps of half tile size to make things smoother */
3070 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3072 Delay(wait_delay_value);
3074 /* scroll second step to align at full tile size */
3076 Delay(wait_delay_value);
3081 Delay(wait_delay_value);
3085 void RelocatePlayer(int jx, int jy, int el_player_raw)
3087 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3088 int player_nr = GET_PLAYER_NR(el_player);
3089 struct PlayerInfo *player = &stored_player[player_nr];
3090 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3091 boolean no_delay = (tape.warp_forward);
3092 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3093 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3094 int old_jx = player->jx;
3095 int old_jy = player->jy;
3096 int old_element = Feld[old_jx][old_jy];
3097 int element = Feld[jx][jy];
3098 boolean player_relocated = (old_jx != jx || old_jy != jy);
3100 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3101 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3102 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3103 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3104 int leave_side_horiz = move_dir_horiz;
3105 int leave_side_vert = move_dir_vert;
3106 int enter_side = enter_side_horiz | enter_side_vert;
3107 int leave_side = leave_side_horiz | leave_side_vert;
3109 if (player->GameOver) /* do not reanimate dead player */
3112 if (!player_relocated) /* no need to relocate the player */
3115 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3117 RemoveField(jx, jy); /* temporarily remove newly placed player */
3118 DrawLevelField(jx, jy);
3121 if (player->present)
3123 while (player->MovPos)
3125 ScrollPlayer(player, SCROLL_GO_ON);
3126 ScrollScreen(NULL, SCROLL_GO_ON);
3128 AdvanceFrameAndPlayerCounters(player->index_nr);
3133 Delay(wait_delay_value);
3136 DrawPlayer(player); /* needed here only to cleanup last field */
3137 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3139 player->is_moving = FALSE;
3142 if (IS_CUSTOM_ELEMENT(old_element))
3143 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3145 player->index_bit, leave_side);
3147 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3149 player->index_bit, leave_side);
3151 Feld[jx][jy] = el_player;
3152 InitPlayerField(jx, jy, el_player, TRUE);
3154 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3156 Feld[jx][jy] = element;
3157 InitField(jx, jy, FALSE);
3160 if (player == local_player) /* only visually relocate local player */
3161 DrawRelocatePlayer(player);
3163 TestIfPlayerTouchesBadThing(jx, jy);
3164 TestIfPlayerTouchesCustomElement(jx, jy);
3166 if (IS_CUSTOM_ELEMENT(element))
3167 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3168 player->index_bit, enter_side);
3170 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3171 player->index_bit, enter_side);
3174 void Explode(int ex, int ey, int phase, int mode)
3180 /* !!! eliminate this variable !!! */
3181 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3183 if (game.explosions_delayed)
3185 ExplodeField[ex][ey] = mode;
3189 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3191 int center_element = Feld[ex][ey];
3192 int artwork_element, explosion_element; /* set these values later */
3195 /* --- This is only really needed (and now handled) in "Impact()". --- */
3196 /* do not explode moving elements that left the explode field in time */
3197 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3198 center_element == EL_EMPTY &&
3199 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3204 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3205 if (mode == EX_TYPE_NORMAL ||
3206 mode == EX_TYPE_CENTER ||
3207 mode == EX_TYPE_CROSS)
3208 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3211 /* remove things displayed in background while burning dynamite */
3212 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3215 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3217 /* put moving element to center field (and let it explode there) */
3218 center_element = MovingOrBlocked2Element(ex, ey);
3219 RemoveMovingField(ex, ey);
3220 Feld[ex][ey] = center_element;
3223 /* now "center_element" is finally determined -- set related values now */
3224 artwork_element = center_element; /* for custom player artwork */
3225 explosion_element = center_element; /* for custom player artwork */
3227 if (IS_PLAYER(ex, ey))
3229 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3231 artwork_element = stored_player[player_nr].artwork_element;
3233 if (level.use_explosion_element[player_nr])
3235 explosion_element = level.explosion_element[player_nr];
3236 artwork_element = explosion_element;
3241 if (mode == EX_TYPE_NORMAL ||
3242 mode == EX_TYPE_CENTER ||
3243 mode == EX_TYPE_CROSS)
3244 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3248 last_phase = element_info[explosion_element].explosion_delay + 1;
3250 last_phase = element_info[center_element].explosion_delay + 1;
3253 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3255 int xx = x - ex + 1;
3256 int yy = y - ey + 1;
3259 if (!IN_LEV_FIELD(x, y) ||
3260 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3261 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3264 element = Feld[x][y];
3266 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3268 element = MovingOrBlocked2Element(x, y);
3270 if (!IS_EXPLOSION_PROOF(element))
3271 RemoveMovingField(x, y);
3274 /* indestructible elements can only explode in center (but not flames) */
3275 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3276 mode == EX_TYPE_BORDER)) ||
3277 element == EL_FLAMES)
3280 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3281 behaviour, for example when touching a yamyam that explodes to rocks
3282 with active deadly shield, a rock is created under the player !!! */
3283 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3285 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3286 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3287 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3289 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3292 if (IS_ACTIVE_BOMB(element))
3294 /* re-activate things under the bomb like gate or penguin */
3295 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3302 /* save walkable background elements while explosion on same tile */
3303 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3304 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3305 Back[x][y] = element;
3307 /* ignite explodable elements reached by other explosion */
3308 if (element == EL_EXPLOSION)
3309 element = Store2[x][y];
3311 if (AmoebaNr[x][y] &&
3312 (element == EL_AMOEBA_FULL ||
3313 element == EL_BD_AMOEBA ||
3314 element == EL_AMOEBA_GROWING))
3316 AmoebaCnt[AmoebaNr[x][y]]--;
3317 AmoebaCnt2[AmoebaNr[x][y]]--;
3322 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3325 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3327 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3329 switch(StorePlayer[ex][ey])
3332 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3335 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3338 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3342 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3347 if (PLAYERINFO(ex, ey)->use_murphy)
3348 Store[x][y] = EL_EMPTY;
3351 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3352 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3353 else if (ELEM_IS_PLAYER(center_element))
3354 Store[x][y] = EL_EMPTY;
3355 else if (center_element == EL_YAMYAM)
3356 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3357 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3358 Store[x][y] = element_info[center_element].content.e[xx][yy];
3360 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3361 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3362 otherwise) -- FIX THIS !!! */
3363 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3364 Store[x][y] = element_info[element].content.e[1][1];
3366 else if (!CAN_EXPLODE(element))
3367 Store[x][y] = element_info[element].content.e[1][1];
3370 Store[x][y] = EL_EMPTY;
3372 else if (center_element == EL_MOLE)
3373 Store[x][y] = EL_EMERALD_RED;
3374 else if (center_element == EL_PENGUIN)
3375 Store[x][y] = EL_EMERALD_PURPLE;
3376 else if (center_element == EL_BUG)
3377 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3378 else if (center_element == EL_BD_BUTTERFLY)
3379 Store[x][y] = EL_BD_DIAMOND;
3380 else if (center_element == EL_SP_ELECTRON)
3381 Store[x][y] = EL_SP_INFOTRON;
3382 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3383 Store[x][y] = level.amoeba_content;
3384 else if (center_element == EL_YAMYAM)
3385 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3386 else if (IS_CUSTOM_ELEMENT(center_element) &&
3387 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3388 Store[x][y] = element_info[center_element].content.e[xx][yy];
3389 else if (element == EL_WALL_EMERALD)
3390 Store[x][y] = EL_EMERALD;
3391 else if (element == EL_WALL_DIAMOND)
3392 Store[x][y] = EL_DIAMOND;
3393 else if (element == EL_WALL_BD_DIAMOND)
3394 Store[x][y] = EL_BD_DIAMOND;
3395 else if (element == EL_WALL_EMERALD_YELLOW)
3396 Store[x][y] = EL_EMERALD_YELLOW;
3397 else if (element == EL_WALL_EMERALD_RED)
3398 Store[x][y] = EL_EMERALD_RED;
3399 else if (element == EL_WALL_EMERALD_PURPLE)
3400 Store[x][y] = EL_EMERALD_PURPLE;
3401 else if (element == EL_WALL_PEARL)
3402 Store[x][y] = EL_PEARL;
3403 else if (element == EL_WALL_CRYSTAL)
3404 Store[x][y] = EL_CRYSTAL;
3405 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3406 Store[x][y] = element_info[element].content.e[1][1];
3408 Store[x][y] = EL_EMPTY;
3411 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3412 center_element == EL_AMOEBA_TO_DIAMOND)
3413 Store2[x][y] = element;
3415 Feld[x][y] = EL_EXPLOSION;
3416 GfxElement[x][y] = artwork_element;
3418 ExplodePhase[x][y] = 1;
3419 ExplodeDelay[x][y] = last_phase;
3424 if (center_element == EL_YAMYAM)
3425 game.yamyam_content_nr =
3426 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3438 GfxFrame[x][y] = 0; /* restart explosion animation */
3440 last_phase = ExplodeDelay[x][y];
3442 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3446 /* activate this even in non-DEBUG version until cause for crash in
3447 getGraphicAnimationFrame() (see below) is found and eliminated */
3452 if (GfxElement[x][y] == EL_UNDEFINED)
3455 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3456 printf("Explode(): This should never happen!\n");
3459 GfxElement[x][y] = EL_EMPTY;
3463 border_element = Store2[x][y];
3464 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3465 border_element = StorePlayer[x][y];
3467 if (phase == element_info[border_element].ignition_delay ||
3468 phase == last_phase)
3470 boolean border_explosion = FALSE;
3472 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3473 !PLAYER_EXPLOSION_PROTECTED(x, y))
3475 KillPlayerUnlessExplosionProtected(x, y);
3476 border_explosion = TRUE;
3478 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3480 Feld[x][y] = Store2[x][y];
3483 border_explosion = TRUE;
3485 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3487 AmoebeUmwandeln(x, y);
3489 border_explosion = TRUE;
3492 /* if an element just explodes due to another explosion (chain-reaction),
3493 do not immediately end the new explosion when it was the last frame of
3494 the explosion (as it would be done in the following "if"-statement!) */
3495 if (border_explosion && phase == last_phase)
3499 if (phase == last_phase)
3503 element = Feld[x][y] = Store[x][y];
3504 Store[x][y] = Store2[x][y] = 0;
3505 GfxElement[x][y] = EL_UNDEFINED;
3507 /* player can escape from explosions and might therefore be still alive */
3508 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3509 element <= EL_PLAYER_IS_EXPLODING_4)
3511 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3512 int explosion_element = EL_PLAYER_1 + player_nr;
3513 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3514 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3516 if (level.use_explosion_element[player_nr])
3517 explosion_element = level.explosion_element[player_nr];
3519 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3520 element_info[explosion_element].content.e[xx][yy]);
3523 /* restore probably existing indestructible background element */
3524 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3525 element = Feld[x][y] = Back[x][y];
3528 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3529 GfxDir[x][y] = MV_NONE;
3530 ChangeDelay[x][y] = 0;
3531 ChangePage[x][y] = -1;
3533 #if USE_NEW_CUSTOM_VALUE
3534 CustomValue[x][y] = 0;
3537 InitField_WithBug2(x, y, FALSE);
3539 DrawLevelField(x, y);
3541 TestIfElementTouchesCustomElement(x, y);
3543 if (GFX_CRUMBLED(element))
3544 DrawLevelFieldCrumbledSandNeighbours(x, y);
3546 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3547 StorePlayer[x][y] = 0;
3549 if (ELEM_IS_PLAYER(element))
3550 RelocatePlayer(x, y, element);
3552 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3554 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3555 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3558 DrawLevelFieldCrumbledSand(x, y);
3560 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3562 DrawLevelElement(x, y, Back[x][y]);
3563 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3565 else if (IS_WALKABLE_UNDER(Back[x][y]))
3567 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3568 DrawLevelElementThruMask(x, y, Back[x][y]);
3570 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3571 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3575 void DynaExplode(int ex, int ey)
3578 int dynabomb_element = Feld[ex][ey];
3579 int dynabomb_size = 1;
3580 boolean dynabomb_xl = FALSE;
3581 struct PlayerInfo *player;
3582 static int xy[4][2] =
3590 if (IS_ACTIVE_BOMB(dynabomb_element))
3592 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3593 dynabomb_size = player->dynabomb_size;
3594 dynabomb_xl = player->dynabomb_xl;
3595 player->dynabombs_left++;
3598 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3600 for (i = 0; i < NUM_DIRECTIONS; i++)
3602 for (j = 1; j <= dynabomb_size; j++)
3604 int x = ex + j * xy[i][0];
3605 int y = ey + j * xy[i][1];
3608 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3611 element = Feld[x][y];
3613 /* do not restart explosions of fields with active bombs */
3614 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3617 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3619 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3620 !IS_DIGGABLE(element) && !dynabomb_xl)
3626 void Bang(int x, int y)
3628 int element = MovingOrBlocked2Element(x, y);
3629 int explosion_type = EX_TYPE_NORMAL;
3631 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3633 struct PlayerInfo *player = PLAYERINFO(x, y);
3635 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3636 player->element_nr);
3638 if (level.use_explosion_element[player->index_nr])
3640 int explosion_element = level.explosion_element[player->index_nr];
3642 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3643 explosion_type = EX_TYPE_CROSS;
3644 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3645 explosion_type = EX_TYPE_CENTER;
3653 case EL_BD_BUTTERFLY:
3656 case EL_DARK_YAMYAM:
3660 RaiseScoreElement(element);
3663 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3664 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3665 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3666 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3667 case EL_DYNABOMB_INCREASE_NUMBER:
3668 case EL_DYNABOMB_INCREASE_SIZE:
3669 case EL_DYNABOMB_INCREASE_POWER:
3670 explosion_type = EX_TYPE_DYNA;
3675 case EL_LAMP_ACTIVE:
3676 case EL_AMOEBA_TO_DIAMOND:
3677 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3678 explosion_type = EX_TYPE_CENTER;
3682 if (element_info[element].explosion_type == EXPLODES_CROSS)
3683 explosion_type = EX_TYPE_CROSS;
3684 else if (element_info[element].explosion_type == EXPLODES_1X1)
3685 explosion_type = EX_TYPE_CENTER;
3689 if (explosion_type == EX_TYPE_DYNA)
3692 Explode(x, y, EX_PHASE_START, explosion_type);
3694 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3697 void SplashAcid(int x, int y)
3699 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3700 (!IN_LEV_FIELD(x - 1, y - 2) ||
3701 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3702 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3704 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3705 (!IN_LEV_FIELD(x + 1, y - 2) ||
3706 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3707 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3709 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3712 static void InitBeltMovement()
3714 static int belt_base_element[4] =
3716 EL_CONVEYOR_BELT_1_LEFT,
3717 EL_CONVEYOR_BELT_2_LEFT,
3718 EL_CONVEYOR_BELT_3_LEFT,
3719 EL_CONVEYOR_BELT_4_LEFT
3721 static int belt_base_active_element[4] =
3723 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3724 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3725 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3726 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3731 /* set frame order for belt animation graphic according to belt direction */
3732 for (i = 0; i < NUM_BELTS; i++)
3736 for (j = 0; j < NUM_BELT_PARTS; j++)
3738 int element = belt_base_active_element[belt_nr] + j;
3739 int graphic = el2img(element);
3741 if (game.belt_dir[i] == MV_LEFT)
3742 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3744 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3749 SCAN_PLAYFIELD(x, y)
3751 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3754 int element = Feld[x][y];
3756 for (i = 0; i < NUM_BELTS; i++)
3758 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3760 int e_belt_nr = getBeltNrFromBeltElement(element);
3763 if (e_belt_nr == belt_nr)
3765 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3767 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3774 static void ToggleBeltSwitch(int x, int y)
3776 static int belt_base_element[4] =
3778 EL_CONVEYOR_BELT_1_LEFT,
3779 EL_CONVEYOR_BELT_2_LEFT,
3780 EL_CONVEYOR_BELT_3_LEFT,
3781 EL_CONVEYOR_BELT_4_LEFT
3783 static int belt_base_active_element[4] =
3785 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3786 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3787 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3788 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3790 static int belt_base_switch_element[4] =
3792 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3793 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3794 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3795 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3797 static int belt_move_dir[4] =
3805 int element = Feld[x][y];
3806 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3807 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3808 int belt_dir = belt_move_dir[belt_dir_nr];
3811 if (!IS_BELT_SWITCH(element))
3814 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3815 game.belt_dir[belt_nr] = belt_dir;
3817 if (belt_dir_nr == 3)
3820 /* set frame order for belt animation graphic according to belt direction */
3821 for (i = 0; i < NUM_BELT_PARTS; i++)
3823 int element = belt_base_active_element[belt_nr] + i;
3824 int graphic = el2img(element);
3826 if (belt_dir == MV_LEFT)
3827 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3829 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3833 SCAN_PLAYFIELD(xx, yy)
3835 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3838 int element = Feld[xx][yy];
3840 if (IS_BELT_SWITCH(element))
3842 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3844 if (e_belt_nr == belt_nr)
3846 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3847 DrawLevelField(xx, yy);
3850 else if (IS_BELT(element) && belt_dir != MV_NONE)
3852 int e_belt_nr = getBeltNrFromBeltElement(element);
3854 if (e_belt_nr == belt_nr)
3856 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3858 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3859 DrawLevelField(xx, yy);
3862 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3864 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3866 if (e_belt_nr == belt_nr)
3868 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3870 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3871 DrawLevelField(xx, yy);
3877 static void ToggleSwitchgateSwitch(int x, int y)
3881 game.switchgate_pos = !game.switchgate_pos;
3884 SCAN_PLAYFIELD(xx, yy)
3886 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3889 int element = Feld[xx][yy];
3891 if (element == EL_SWITCHGATE_SWITCH_UP ||
3892 element == EL_SWITCHGATE_SWITCH_DOWN)
3894 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3895 DrawLevelField(xx, yy);
3897 else if (element == EL_SWITCHGATE_OPEN ||
3898 element == EL_SWITCHGATE_OPENING)
3900 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3902 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3904 else if (element == EL_SWITCHGATE_CLOSED ||
3905 element == EL_SWITCHGATE_CLOSING)
3907 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3909 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3914 static int getInvisibleActiveFromInvisibleElement(int element)
3916 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3917 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3918 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3922 static int getInvisibleFromInvisibleActiveElement(int element)
3924 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3925 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3926 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3930 static void RedrawAllLightSwitchesAndInvisibleElements()
3935 SCAN_PLAYFIELD(x, y)
3937 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3940 int element = Feld[x][y];
3942 if (element == EL_LIGHT_SWITCH &&
3943 game.light_time_left > 0)
3945 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3946 DrawLevelField(x, y);
3948 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3949 game.light_time_left == 0)
3951 Feld[x][y] = EL_LIGHT_SWITCH;
3952 DrawLevelField(x, y);
3954 else if (element == EL_EMC_DRIPPER &&
3955 game.light_time_left > 0)
3957 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
3958 DrawLevelField(x, y);
3960 else if (element == EL_EMC_DRIPPER_ACTIVE &&
3961 game.light_time_left == 0)
3963 Feld[x][y] = EL_EMC_DRIPPER;
3964 DrawLevelField(x, y);
3966 else if (element == EL_INVISIBLE_STEELWALL ||
3967 element == EL_INVISIBLE_WALL ||
3968 element == EL_INVISIBLE_SAND)
3970 if (game.light_time_left > 0)
3971 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3973 DrawLevelField(x, y);
3975 /* uncrumble neighbour fields, if needed */
3976 if (element == EL_INVISIBLE_SAND)
3977 DrawLevelFieldCrumbledSandNeighbours(x, y);
3979 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3980 element == EL_INVISIBLE_WALL_ACTIVE ||
3981 element == EL_INVISIBLE_SAND_ACTIVE)
3983 if (game.light_time_left == 0)
3984 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3986 DrawLevelField(x, y);
3988 /* re-crumble neighbour fields, if needed */
3989 if (element == EL_INVISIBLE_SAND)
3990 DrawLevelFieldCrumbledSandNeighbours(x, y);
3995 static void RedrawAllInvisibleElementsForLenses()
4000 SCAN_PLAYFIELD(x, y)
4002 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4005 int element = Feld[x][y];
4007 if (element == EL_EMC_DRIPPER &&
4008 game.lenses_time_left > 0)
4010 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4011 DrawLevelField(x, y);
4013 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4014 game.lenses_time_left == 0)
4016 Feld[x][y] = EL_EMC_DRIPPER;
4017 DrawLevelField(x, y);
4019 else if (element == EL_INVISIBLE_STEELWALL ||
4020 element == EL_INVISIBLE_WALL ||
4021 element == EL_INVISIBLE_SAND)
4023 if (game.lenses_time_left > 0)
4024 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4026 DrawLevelField(x, y);
4028 /* uncrumble neighbour fields, if needed */
4029 if (element == EL_INVISIBLE_SAND)
4030 DrawLevelFieldCrumbledSandNeighbours(x, y);
4032 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4033 element == EL_INVISIBLE_WALL_ACTIVE ||
4034 element == EL_INVISIBLE_SAND_ACTIVE)
4036 if (game.lenses_time_left == 0)
4037 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4039 DrawLevelField(x, y);
4041 /* re-crumble neighbour fields, if needed */
4042 if (element == EL_INVISIBLE_SAND)
4043 DrawLevelFieldCrumbledSandNeighbours(x, y);
4048 static void RedrawAllInvisibleElementsForMagnifier()
4053 SCAN_PLAYFIELD(x, y)
4055 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4058 int element = Feld[x][y];
4060 if (element == EL_EMC_FAKE_GRASS &&
4061 game.magnify_time_left > 0)
4063 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4064 DrawLevelField(x, y);
4066 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4067 game.magnify_time_left == 0)
4069 Feld[x][y] = EL_EMC_FAKE_GRASS;
4070 DrawLevelField(x, y);
4072 else if (IS_GATE_GRAY(element) &&
4073 game.magnify_time_left > 0)
4075 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4076 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4077 IS_EM_GATE_GRAY(element) ?
4078 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4079 IS_EMC_GATE_GRAY(element) ?
4080 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4082 DrawLevelField(x, y);
4084 else if (IS_GATE_GRAY_ACTIVE(element) &&
4085 game.magnify_time_left == 0)
4087 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4088 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4089 IS_EM_GATE_GRAY_ACTIVE(element) ?
4090 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4091 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4092 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4094 DrawLevelField(x, y);
4099 static void ToggleLightSwitch(int x, int y)
4101 int element = Feld[x][y];
4103 game.light_time_left =
4104 (element == EL_LIGHT_SWITCH ?
4105 level.time_light * FRAMES_PER_SECOND : 0);
4107 RedrawAllLightSwitchesAndInvisibleElements();
4110 static void ActivateTimegateSwitch(int x, int y)
4114 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4117 SCAN_PLAYFIELD(xx, yy)
4119 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4122 int element = Feld[xx][yy];
4124 if (element == EL_TIMEGATE_CLOSED ||
4125 element == EL_TIMEGATE_CLOSING)
4127 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4128 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4132 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4134 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4135 DrawLevelField(xx, yy);
4141 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4144 void Impact(int x, int y)
4146 boolean last_line = (y == lev_fieldy - 1);
4147 boolean object_hit = FALSE;
4148 boolean impact = (last_line || object_hit);
4149 int element = Feld[x][y];
4150 int smashed = EL_STEELWALL;
4152 if (!last_line) /* check if element below was hit */
4154 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4157 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4158 MovDir[x][y + 1] != MV_DOWN ||
4159 MovPos[x][y + 1] <= TILEY / 2));
4161 /* do not smash moving elements that left the smashed field in time */
4162 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4163 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4166 #if USE_QUICKSAND_IMPACT_BUGFIX
4167 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4169 RemoveMovingField(x, y + 1);
4170 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4171 Feld[x][y + 2] = EL_ROCK;
4172 DrawLevelField(x, y + 2);
4179 smashed = MovingOrBlocked2Element(x, y + 1);
4181 impact = (last_line || object_hit);
4184 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4186 SplashAcid(x, y + 1);
4190 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4191 /* only reset graphic animation if graphic really changes after impact */
4193 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4195 ResetGfxAnimation(x, y);
4196 DrawLevelField(x, y);
4199 if (impact && CAN_EXPLODE_IMPACT(element))
4204 else if (impact && element == EL_PEARL)
4206 ResetGfxAnimation(x, y);
4208 Feld[x][y] = EL_PEARL_BREAKING;
4209 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4212 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4214 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4219 if (impact && element == EL_AMOEBA_DROP)
4221 if (object_hit && IS_PLAYER(x, y + 1))
4222 KillPlayerUnlessEnemyProtected(x, y + 1);
4223 else if (object_hit && smashed == EL_PENGUIN)
4227 Feld[x][y] = EL_AMOEBA_GROWING;
4228 Store[x][y] = EL_AMOEBA_WET;
4230 ResetRandomAnimationValue(x, y);
4235 if (object_hit) /* check which object was hit */
4237 if (CAN_PASS_MAGIC_WALL(element) &&
4238 (smashed == EL_MAGIC_WALL ||
4239 smashed == EL_BD_MAGIC_WALL))
4242 int activated_magic_wall =
4243 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4244 EL_BD_MAGIC_WALL_ACTIVE);
4246 /* activate magic wall / mill */
4248 SCAN_PLAYFIELD(xx, yy)
4250 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4252 if (Feld[xx][yy] == smashed)
4253 Feld[xx][yy] = activated_magic_wall;
4255 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4256 game.magic_wall_active = TRUE;
4258 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4259 SND_MAGIC_WALL_ACTIVATING :
4260 SND_BD_MAGIC_WALL_ACTIVATING));
4263 if (IS_PLAYER(x, y + 1))
4265 if (CAN_SMASH_PLAYER(element))
4267 KillPlayerUnlessEnemyProtected(x, y + 1);
4271 else if (smashed == EL_PENGUIN)
4273 if (CAN_SMASH_PLAYER(element))
4279 else if (element == EL_BD_DIAMOND)
4281 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4287 else if (((element == EL_SP_INFOTRON ||
4288 element == EL_SP_ZONK) &&
4289 (smashed == EL_SP_SNIKSNAK ||
4290 smashed == EL_SP_ELECTRON ||
4291 smashed == EL_SP_DISK_ORANGE)) ||
4292 (element == EL_SP_INFOTRON &&
4293 smashed == EL_SP_DISK_YELLOW))
4298 else if (CAN_SMASH_EVERYTHING(element))
4300 if (IS_CLASSIC_ENEMY(smashed) ||
4301 CAN_EXPLODE_SMASHED(smashed))
4306 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4308 if (smashed == EL_LAMP ||
4309 smashed == EL_LAMP_ACTIVE)
4314 else if (smashed == EL_NUT)
4316 Feld[x][y + 1] = EL_NUT_BREAKING;
4317 PlayLevelSound(x, y, SND_NUT_BREAKING);
4318 RaiseScoreElement(EL_NUT);
4321 else if (smashed == EL_PEARL)
4323 ResetGfxAnimation(x, y);
4325 Feld[x][y + 1] = EL_PEARL_BREAKING;
4326 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4329 else if (smashed == EL_DIAMOND)
4331 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4332 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4335 else if (IS_BELT_SWITCH(smashed))
4337 ToggleBeltSwitch(x, y + 1);
4339 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4340 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4342 ToggleSwitchgateSwitch(x, y + 1);
4344 else if (smashed == EL_LIGHT_SWITCH ||
4345 smashed == EL_LIGHT_SWITCH_ACTIVE)
4347 ToggleLightSwitch(x, y + 1);
4352 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4355 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4357 CheckElementChangeBySide(x, y + 1, smashed, element,
4358 CE_SWITCHED, CH_SIDE_TOP);
4359 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4365 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4370 /* play sound of magic wall / mill */
4372 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4373 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4375 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4376 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4377 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4378 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4383 /* play sound of object that hits the ground */
4384 if (last_line || object_hit)
4385 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4388 inline static void TurnRoundExt(int x, int y)
4400 { 0, 0 }, { 0, 0 }, { 0, 0 },
4405 int left, right, back;
4409 { MV_DOWN, MV_UP, MV_RIGHT },
4410 { MV_UP, MV_DOWN, MV_LEFT },
4412 { MV_LEFT, MV_RIGHT, MV_DOWN },
4416 { MV_RIGHT, MV_LEFT, MV_UP }
4419 int element = Feld[x][y];
4420 int move_pattern = element_info[element].move_pattern;
4422 int old_move_dir = MovDir[x][y];
4423 int left_dir = turn[old_move_dir].left;
4424 int right_dir = turn[old_move_dir].right;
4425 int back_dir = turn[old_move_dir].back;
4427 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4428 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4429 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4430 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4432 int left_x = x + left_dx, left_y = y + left_dy;
4433 int right_x = x + right_dx, right_y = y + right_dy;
4434 int move_x = x + move_dx, move_y = y + move_dy;
4438 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4440 TestIfBadThingTouchesOtherBadThing(x, y);
4442 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4443 MovDir[x][y] = right_dir;
4444 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4445 MovDir[x][y] = left_dir;
4447 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4449 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4452 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4454 TestIfBadThingTouchesOtherBadThing(x, y);
4456 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4457 MovDir[x][y] = left_dir;
4458 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4459 MovDir[x][y] = right_dir;
4461 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4463 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4466 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4468 TestIfBadThingTouchesOtherBadThing(x, y);
4470 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4471 MovDir[x][y] = left_dir;
4472 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4473 MovDir[x][y] = right_dir;
4475 if (MovDir[x][y] != old_move_dir)
4478 else if (element == EL_YAMYAM)
4480 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4481 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4483 if (can_turn_left && can_turn_right)
4484 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4485 else if (can_turn_left)
4486 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4487 else if (can_turn_right)
4488 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4490 MovDir[x][y] = back_dir;
4492 MovDelay[x][y] = 16 + 16 * RND(3);
4494 else if (element == EL_DARK_YAMYAM)
4496 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4498 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4501 if (can_turn_left && can_turn_right)
4502 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4503 else if (can_turn_left)
4504 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4505 else if (can_turn_right)
4506 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4508 MovDir[x][y] = back_dir;
4510 MovDelay[x][y] = 16 + 16 * RND(3);
4512 else if (element == EL_PACMAN)
4514 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4515 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4517 if (can_turn_left && can_turn_right)
4518 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4519 else if (can_turn_left)
4520 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4521 else if (can_turn_right)
4522 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4524 MovDir[x][y] = back_dir;
4526 MovDelay[x][y] = 6 + RND(40);
4528 else if (element == EL_PIG)
4530 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4531 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4532 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4533 boolean should_turn_left, should_turn_right, should_move_on;
4535 int rnd = RND(rnd_value);
4537 should_turn_left = (can_turn_left &&
4539 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4540 y + back_dy + left_dy)));
4541 should_turn_right = (can_turn_right &&
4543 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4544 y + back_dy + right_dy)));
4545 should_move_on = (can_move_on &&
4548 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4549 y + move_dy + left_dy) ||
4550 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4551 y + move_dy + right_dy)));
4553 if (should_turn_left || should_turn_right || should_move_on)
4555 if (should_turn_left && should_turn_right && should_move_on)
4556 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4557 rnd < 2 * rnd_value / 3 ? right_dir :
4559 else if (should_turn_left && should_turn_right)
4560 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4561 else if (should_turn_left && should_move_on)
4562 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4563 else if (should_turn_right && should_move_on)
4564 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4565 else if (should_turn_left)
4566 MovDir[x][y] = left_dir;
4567 else if (should_turn_right)
4568 MovDir[x][y] = right_dir;
4569 else if (should_move_on)
4570 MovDir[x][y] = old_move_dir;
4572 else if (can_move_on && rnd > rnd_value / 8)
4573 MovDir[x][y] = old_move_dir;
4574 else if (can_turn_left && can_turn_right)
4575 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4576 else if (can_turn_left && rnd > rnd_value / 8)
4577 MovDir[x][y] = left_dir;
4578 else if (can_turn_right && rnd > rnd_value/8)
4579 MovDir[x][y] = right_dir;
4581 MovDir[x][y] = back_dir;
4583 xx = x + move_xy[MovDir[x][y]].dx;
4584 yy = y + move_xy[MovDir[x][y]].dy;
4586 if (!IN_LEV_FIELD(xx, yy) ||
4587 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4588 MovDir[x][y] = old_move_dir;
4592 else if (element == EL_DRAGON)
4594 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4595 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4596 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4598 int rnd = RND(rnd_value);
4600 if (can_move_on && rnd > rnd_value / 8)
4601 MovDir[x][y] = old_move_dir;
4602 else if (can_turn_left && can_turn_right)
4603 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4604 else if (can_turn_left && rnd > rnd_value / 8)
4605 MovDir[x][y] = left_dir;
4606 else if (can_turn_right && rnd > rnd_value / 8)
4607 MovDir[x][y] = right_dir;
4609 MovDir[x][y] = back_dir;
4611 xx = x + move_xy[MovDir[x][y]].dx;
4612 yy = y + move_xy[MovDir[x][y]].dy;
4614 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4615 MovDir[x][y] = old_move_dir;
4619 else if (element == EL_MOLE)
4621 boolean can_move_on =
4622 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4623 IS_AMOEBOID(Feld[move_x][move_y]) ||
4624 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4627 boolean can_turn_left =
4628 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4629 IS_AMOEBOID(Feld[left_x][left_y])));
4631 boolean can_turn_right =
4632 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4633 IS_AMOEBOID(Feld[right_x][right_y])));
4635 if (can_turn_left && can_turn_right)
4636 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4637 else if (can_turn_left)
4638 MovDir[x][y] = left_dir;
4640 MovDir[x][y] = right_dir;
4643 if (MovDir[x][y] != old_move_dir)
4646 else if (element == EL_BALLOON)
4648 MovDir[x][y] = game.wind_direction;
4651 else if (element == EL_SPRING)
4653 if (MovDir[x][y] & MV_HORIZONTAL &&
4654 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4655 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4656 MovDir[x][y] = MV_NONE;
4660 else if (element == EL_ROBOT ||
4661 element == EL_SATELLITE ||
4662 element == EL_PENGUIN)
4664 int attr_x = -1, attr_y = -1;
4675 for (i = 0; i < MAX_PLAYERS; i++)
4677 struct PlayerInfo *player = &stored_player[i];
4678 int jx = player->jx, jy = player->jy;
4680 if (!player->active)
4684 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4692 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4693 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4694 game.engine_version < VERSION_IDENT(3,1,0,0)))
4700 if (element == EL_PENGUIN)
4703 static int xy[4][2] =
4711 for (i = 0; i < NUM_DIRECTIONS; i++)
4713 int ex = x + xy[i][0];
4714 int ey = y + xy[i][1];
4716 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4725 MovDir[x][y] = MV_NONE;
4727 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4728 else if (attr_x > x)
4729 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4731 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4732 else if (attr_y > y)
4733 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4735 if (element == EL_ROBOT)
4739 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4740 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4741 Moving2Blocked(x, y, &newx, &newy);
4743 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4744 MovDelay[x][y] = 8 + 8 * !RND(3);
4746 MovDelay[x][y] = 16;
4748 else if (element == EL_PENGUIN)
4754 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4756 boolean first_horiz = RND(2);
4757 int new_move_dir = MovDir[x][y];
4760 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4761 Moving2Blocked(x, y, &newx, &newy);
4763 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4767 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4768 Moving2Blocked(x, y, &newx, &newy);
4770 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4773 MovDir[x][y] = old_move_dir;
4777 else /* (element == EL_SATELLITE) */
4783 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4785 boolean first_horiz = RND(2);
4786 int new_move_dir = MovDir[x][y];
4789 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4790 Moving2Blocked(x, y, &newx, &newy);
4792 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4796 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4797 Moving2Blocked(x, y, &newx, &newy);
4799 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4802 MovDir[x][y] = old_move_dir;
4807 else if (move_pattern == MV_TURNING_LEFT ||
4808 move_pattern == MV_TURNING_RIGHT ||
4809 move_pattern == MV_TURNING_LEFT_RIGHT ||
4810 move_pattern == MV_TURNING_RIGHT_LEFT ||
4811 move_pattern == MV_TURNING_RANDOM ||
4812 move_pattern == MV_ALL_DIRECTIONS)
4814 boolean can_turn_left =
4815 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4816 boolean can_turn_right =
4817 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4819 if (element_info[element].move_stepsize == 0) /* "not moving" */
4822 if (move_pattern == MV_TURNING_LEFT)
4823 MovDir[x][y] = left_dir;
4824 else if (move_pattern == MV_TURNING_RIGHT)
4825 MovDir[x][y] = right_dir;
4826 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4827 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4828 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4829 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4830 else if (move_pattern == MV_TURNING_RANDOM)
4831 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4832 can_turn_right && !can_turn_left ? right_dir :
4833 RND(2) ? left_dir : right_dir);
4834 else if (can_turn_left && can_turn_right)
4835 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4836 else if (can_turn_left)
4837 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4838 else if (can_turn_right)
4839 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4841 MovDir[x][y] = back_dir;
4843 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4845 else if (move_pattern == MV_HORIZONTAL ||
4846 move_pattern == MV_VERTICAL)
4848 if (move_pattern & old_move_dir)
4849 MovDir[x][y] = back_dir;
4850 else if (move_pattern == MV_HORIZONTAL)
4851 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4852 else if (move_pattern == MV_VERTICAL)
4853 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4855 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4857 else if (move_pattern & MV_ANY_DIRECTION)
4859 MovDir[x][y] = move_pattern;
4860 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4862 else if (move_pattern & MV_WIND_DIRECTION)
4864 MovDir[x][y] = game.wind_direction;
4865 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4867 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4869 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4870 MovDir[x][y] = left_dir;
4871 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4872 MovDir[x][y] = right_dir;
4874 if (MovDir[x][y] != old_move_dir)
4875 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4877 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4879 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4880 MovDir[x][y] = right_dir;
4881 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4882 MovDir[x][y] = left_dir;
4884 if (MovDir[x][y] != old_move_dir)
4885 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4887 else if (move_pattern == MV_TOWARDS_PLAYER ||
4888 move_pattern == MV_AWAY_FROM_PLAYER)
4890 int attr_x = -1, attr_y = -1;
4892 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4903 for (i = 0; i < MAX_PLAYERS; i++)
4905 struct PlayerInfo *player = &stored_player[i];
4906 int jx = player->jx, jy = player->jy;
4908 if (!player->active)
4912 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4920 MovDir[x][y] = MV_NONE;
4922 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4923 else if (attr_x > x)
4924 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4926 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4927 else if (attr_y > y)
4928 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4930 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4932 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4934 boolean first_horiz = RND(2);
4935 int new_move_dir = MovDir[x][y];
4937 if (element_info[element].move_stepsize == 0) /* "not moving" */
4939 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4940 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4946 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4947 Moving2Blocked(x, y, &newx, &newy);
4949 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4953 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4954 Moving2Blocked(x, y, &newx, &newy);
4956 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4959 MovDir[x][y] = old_move_dir;
4962 else if (move_pattern == MV_WHEN_PUSHED ||
4963 move_pattern == MV_WHEN_DROPPED)
4965 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4966 MovDir[x][y] = MV_NONE;
4970 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4972 static int test_xy[7][2] =
4982 static int test_dir[7] =
4992 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4993 int move_preference = -1000000; /* start with very low preference */
4994 int new_move_dir = MV_NONE;
4995 int start_test = RND(4);
4998 for (i = 0; i < NUM_DIRECTIONS; i++)
5000 int move_dir = test_dir[start_test + i];
5001 int move_dir_preference;
5003 xx = x + test_xy[start_test + i][0];
5004 yy = y + test_xy[start_test + i][1];
5006 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5007 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5009 new_move_dir = move_dir;
5014 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5017 move_dir_preference = -1 * RunnerVisit[xx][yy];
5018 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5019 move_dir_preference = PlayerVisit[xx][yy];
5021 if (move_dir_preference > move_preference)
5023 /* prefer field that has not been visited for the longest time */
5024 move_preference = move_dir_preference;
5025 new_move_dir = move_dir;
5027 else if (move_dir_preference == move_preference &&
5028 move_dir == old_move_dir)
5030 /* prefer last direction when all directions are preferred equally */
5031 move_preference = move_dir_preference;
5032 new_move_dir = move_dir;
5036 MovDir[x][y] = new_move_dir;
5037 if (old_move_dir != new_move_dir)
5038 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5042 static void TurnRound(int x, int y)
5044 int direction = MovDir[x][y];
5048 GfxDir[x][y] = MovDir[x][y];
5050 if (direction != MovDir[x][y])
5054 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5057 static boolean JustBeingPushed(int x, int y)
5061 for (i = 0; i < MAX_PLAYERS; i++)
5063 struct PlayerInfo *player = &stored_player[i];
5065 if (player->active && player->is_pushing && player->MovPos)
5067 int next_jx = player->jx + (player->jx - player->last_jx);
5068 int next_jy = player->jy + (player->jy - player->last_jy);
5070 if (x == next_jx && y == next_jy)
5078 void StartMoving(int x, int y)
5080 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5081 int element = Feld[x][y];
5086 if (MovDelay[x][y] == 0)
5087 GfxAction[x][y] = ACTION_DEFAULT;
5089 if (CAN_FALL(element) && y < lev_fieldy - 1)
5091 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5092 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5093 if (JustBeingPushed(x, y))
5096 if (element == EL_QUICKSAND_FULL)
5098 if (IS_FREE(x, y + 1))
5100 InitMovingField(x, y, MV_DOWN);
5101 started_moving = TRUE;
5103 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5104 Store[x][y] = EL_ROCK;
5106 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5108 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5110 if (!MovDelay[x][y])
5111 MovDelay[x][y] = TILEY + 1;
5120 Feld[x][y] = EL_QUICKSAND_EMPTY;
5121 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5122 Store[x][y + 1] = Store[x][y];
5125 PlayLevelSoundAction(x, y, ACTION_FILLING);
5128 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5129 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5131 InitMovingField(x, y, MV_DOWN);
5132 started_moving = TRUE;
5134 Feld[x][y] = EL_QUICKSAND_FILLING;
5135 Store[x][y] = element;
5137 PlayLevelSoundAction(x, y, ACTION_FILLING);
5139 else if (element == EL_MAGIC_WALL_FULL)
5141 if (IS_FREE(x, y + 1))
5143 InitMovingField(x, y, MV_DOWN);
5144 started_moving = TRUE;
5146 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5147 Store[x][y] = EL_CHANGED(Store[x][y]);
5149 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5151 if (!MovDelay[x][y])
5152 MovDelay[x][y] = TILEY/4 + 1;
5161 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5162 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5163 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5167 else if (element == EL_BD_MAGIC_WALL_FULL)
5169 if (IS_FREE(x, y + 1))
5171 InitMovingField(x, y, MV_DOWN);
5172 started_moving = TRUE;
5174 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5175 Store[x][y] = EL_CHANGED2(Store[x][y]);
5177 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5179 if (!MovDelay[x][y])
5180 MovDelay[x][y] = TILEY/4 + 1;
5189 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5190 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5191 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5195 else if (CAN_PASS_MAGIC_WALL(element) &&
5196 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5197 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5199 InitMovingField(x, y, MV_DOWN);
5200 started_moving = TRUE;
5203 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5204 EL_BD_MAGIC_WALL_FILLING);
5205 Store[x][y] = element;
5207 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5209 SplashAcid(x, y + 1);
5211 InitMovingField(x, y, MV_DOWN);
5212 started_moving = TRUE;
5214 Store[x][y] = EL_ACID;
5216 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5217 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5219 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5220 CAN_FALL(element) && WasJustFalling[x][y] &&
5221 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5223 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5224 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5225 (Feld[x][y + 1] == EL_BLOCKED)))
5227 /* this is needed for a special case not covered by calling "Impact()"
5228 from "ContinueMoving()": if an element moves to a tile directly below
5229 another element which was just falling on that tile (which was empty
5230 in the previous frame), the falling element above would just stop
5231 instead of smashing the element below (in previous version, the above
5232 element was just checked for "moving" instead of "falling", resulting
5233 in incorrect smashes caused by horizontal movement of the above
5234 element; also, the case of the player being the element to smash was
5235 simply not covered here... :-/ ) */
5237 CheckCollision[x][y] = 0;
5241 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5243 if (MovDir[x][y] == MV_NONE)
5245 InitMovingField(x, y, MV_DOWN);
5246 started_moving = TRUE;
5249 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5251 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5252 MovDir[x][y] = MV_DOWN;
5254 InitMovingField(x, y, MV_DOWN);
5255 started_moving = TRUE;
5257 else if (element == EL_AMOEBA_DROP)
5259 Feld[x][y] = EL_AMOEBA_GROWING;
5260 Store[x][y] = EL_AMOEBA_WET;
5262 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5263 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5264 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5265 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5267 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5268 (IS_FREE(x - 1, y + 1) ||
5269 Feld[x - 1][y + 1] == EL_ACID));
5270 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5271 (IS_FREE(x + 1, y + 1) ||
5272 Feld[x + 1][y + 1] == EL_ACID));
5273 boolean can_fall_any = (can_fall_left || can_fall_right);
5274 boolean can_fall_both = (can_fall_left && can_fall_right);
5275 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5277 #if USE_NEW_ALL_SLIPPERY
5278 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5280 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5281 can_fall_right = FALSE;
5282 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5283 can_fall_left = FALSE;
5284 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5285 can_fall_right = FALSE;
5286 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5287 can_fall_left = FALSE;
5289 can_fall_any = (can_fall_left || can_fall_right);
5290 can_fall_both = FALSE;
5293 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5295 if (slippery_type == SLIPPERY_ONLY_LEFT)
5296 can_fall_right = FALSE;
5297 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5298 can_fall_left = FALSE;
5299 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5300 can_fall_right = FALSE;
5301 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5302 can_fall_left = FALSE;
5304 can_fall_any = (can_fall_left || can_fall_right);
5305 can_fall_both = (can_fall_left && can_fall_right);
5309 #if USE_NEW_ALL_SLIPPERY
5311 #if USE_NEW_SP_SLIPPERY
5312 /* !!! better use the same properties as for custom elements here !!! */
5313 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5314 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5316 can_fall_right = FALSE; /* slip down on left side */
5317 can_fall_both = FALSE;
5322 #if USE_NEW_ALL_SLIPPERY
5325 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5326 can_fall_right = FALSE; /* slip down on left side */
5328 can_fall_left = !(can_fall_right = RND(2));
5330 can_fall_both = FALSE;
5335 if (game.emulation == EMU_BOULDERDASH ||
5336 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5337 can_fall_right = FALSE; /* slip down on left side */
5339 can_fall_left = !(can_fall_right = RND(2));
5341 can_fall_both = FALSE;
5347 /* if not determined otherwise, prefer left side for slipping down */
5348 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5349 started_moving = TRUE;
5353 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5355 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5358 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5359 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5360 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5361 int belt_dir = game.belt_dir[belt_nr];
5363 if ((belt_dir == MV_LEFT && left_is_free) ||
5364 (belt_dir == MV_RIGHT && right_is_free))
5366 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5368 InitMovingField(x, y, belt_dir);
5369 started_moving = TRUE;
5371 Pushed[x][y] = TRUE;
5372 Pushed[nextx][y] = TRUE;
5374 GfxAction[x][y] = ACTION_DEFAULT;
5378 MovDir[x][y] = 0; /* if element was moving, stop it */
5383 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5385 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5387 if (CAN_MOVE(element) && !started_moving)
5390 int move_pattern = element_info[element].move_pattern;
5395 if (MovDir[x][y] == MV_NONE)
5397 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5398 x, y, element, element_info[element].token_name);
5399 printf("StartMoving(): This should never happen!\n");
5404 Moving2Blocked(x, y, &newx, &newy);
5406 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5409 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5410 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5412 WasJustMoving[x][y] = 0;
5413 CheckCollision[x][y] = 0;
5415 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5417 if (Feld[x][y] != element) /* element has changed */
5421 if (!MovDelay[x][y]) /* start new movement phase */
5423 /* all objects that can change their move direction after each step
5424 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5426 if (element != EL_YAMYAM &&
5427 element != EL_DARK_YAMYAM &&
5428 element != EL_PACMAN &&
5429 !(move_pattern & MV_ANY_DIRECTION) &&
5430 move_pattern != MV_TURNING_LEFT &&
5431 move_pattern != MV_TURNING_RIGHT &&
5432 move_pattern != MV_TURNING_LEFT_RIGHT &&
5433 move_pattern != MV_TURNING_RIGHT_LEFT &&
5434 move_pattern != MV_TURNING_RANDOM)
5438 if (MovDelay[x][y] && (element == EL_BUG ||
5439 element == EL_SPACESHIP ||
5440 element == EL_SP_SNIKSNAK ||
5441 element == EL_SP_ELECTRON ||
5442 element == EL_MOLE))
5443 DrawLevelField(x, y);
5447 if (MovDelay[x][y]) /* wait some time before next movement */
5451 if (element == EL_ROBOT ||
5452 element == EL_YAMYAM ||
5453 element == EL_DARK_YAMYAM)
5455 DrawLevelElementAnimationIfNeeded(x, y, element);
5456 PlayLevelSoundAction(x, y, ACTION_WAITING);
5458 else if (element == EL_SP_ELECTRON)
5459 DrawLevelElementAnimationIfNeeded(x, y, element);
5460 else if (element == EL_DRAGON)
5463 int dir = MovDir[x][y];
5464 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5465 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5466 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5467 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5468 dir == MV_UP ? IMG_FLAMES_1_UP :
5469 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5470 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5472 GfxAction[x][y] = ACTION_ATTACKING;
5474 if (IS_PLAYER(x, y))
5475 DrawPlayerField(x, y);
5477 DrawLevelField(x, y);
5479 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5481 for (i = 1; i <= 3; i++)
5483 int xx = x + i * dx;
5484 int yy = y + i * dy;
5485 int sx = SCREENX(xx);
5486 int sy = SCREENY(yy);
5487 int flame_graphic = graphic + (i - 1);
5489 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5494 int flamed = MovingOrBlocked2Element(xx, yy);
5498 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5500 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5501 RemoveMovingField(xx, yy);
5503 RemoveField(xx, yy);
5505 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5508 RemoveMovingField(xx, yy);
5511 ChangeDelay[xx][yy] = 0;
5513 Feld[xx][yy] = EL_FLAMES;
5515 if (IN_SCR_FIELD(sx, sy))
5517 DrawLevelFieldCrumbledSand(xx, yy);
5518 DrawGraphic(sx, sy, flame_graphic, frame);
5523 if (Feld[xx][yy] == EL_FLAMES)
5524 Feld[xx][yy] = EL_EMPTY;
5525 DrawLevelField(xx, yy);
5530 if (MovDelay[x][y]) /* element still has to wait some time */
5532 PlayLevelSoundAction(x, y, ACTION_WAITING);
5538 /* now make next step */
5540 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5542 if (DONT_COLLIDE_WITH(element) &&
5543 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5544 !PLAYER_ENEMY_PROTECTED(newx, newy))
5546 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5551 else if (CAN_MOVE_INTO_ACID(element) &&
5552 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5553 (MovDir[x][y] == MV_DOWN ||
5554 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5556 SplashAcid(newx, newy);
5557 Store[x][y] = EL_ACID;
5559 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5561 if (Feld[newx][newy] == EL_EXIT_OPEN)
5564 DrawLevelField(x, y);
5566 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5567 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5568 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5570 local_player->friends_still_needed--;
5571 if (!local_player->friends_still_needed &&
5572 !local_player->GameOver && AllPlayersGone)
5573 local_player->LevelSolved = local_player->GameOver = TRUE;
5577 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5579 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5580 DrawLevelField(newx, newy);
5582 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5584 else if (!IS_FREE(newx, newy))
5586 GfxAction[x][y] = ACTION_WAITING;
5588 if (IS_PLAYER(x, y))
5589 DrawPlayerField(x, y);
5591 DrawLevelField(x, y);
5596 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5598 if (IS_FOOD_PIG(Feld[newx][newy]))
5600 if (IS_MOVING(newx, newy))
5601 RemoveMovingField(newx, newy);
5604 Feld[newx][newy] = EL_EMPTY;
5605 DrawLevelField(newx, newy);
5608 PlayLevelSound(x, y, SND_PIG_DIGGING);
5610 else if (!IS_FREE(newx, newy))
5612 if (IS_PLAYER(x, y))
5613 DrawPlayerField(x, y);
5615 DrawLevelField(x, y);
5620 else if (IS_CUSTOM_ELEMENT(element) &&
5621 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5623 int new_element = Feld[newx][newy];
5625 if (!IS_FREE(newx, newy))
5627 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5628 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5631 /* no element can dig solid indestructible elements */
5632 if (IS_INDESTRUCTIBLE(new_element) &&
5633 !IS_DIGGABLE(new_element) &&
5634 !IS_COLLECTIBLE(new_element))
5637 if (AmoebaNr[newx][newy] &&
5638 (new_element == EL_AMOEBA_FULL ||
5639 new_element == EL_BD_AMOEBA ||
5640 new_element == EL_AMOEBA_GROWING))
5642 AmoebaCnt[AmoebaNr[newx][newy]]--;
5643 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5646 if (IS_MOVING(newx, newy))
5647 RemoveMovingField(newx, newy);
5650 RemoveField(newx, newy);
5651 DrawLevelField(newx, newy);
5654 /* if digged element was about to explode, prevent the explosion */
5655 ExplodeField[newx][newy] = EX_TYPE_NONE;
5657 PlayLevelSoundAction(x, y, action);
5660 Store[newx][newy] = EL_EMPTY;
5662 /* this makes it possible to leave the removed element again */
5663 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5664 Store[newx][newy] = new_element;
5666 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5668 int move_leave_element = element_info[element].move_leave_element;
5670 /* this makes it possible to leave the removed element again */
5671 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5672 new_element : move_leave_element);
5676 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5678 RunnerVisit[x][y] = FrameCounter;
5679 PlayerVisit[x][y] /= 8; /* expire player visit path */
5682 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5684 if (!IS_FREE(newx, newy))
5686 if (IS_PLAYER(x, y))
5687 DrawPlayerField(x, y);
5689 DrawLevelField(x, y);
5695 boolean wanna_flame = !RND(10);
5696 int dx = newx - x, dy = newy - y;
5697 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5698 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5699 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5700 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5701 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5702 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5705 IS_CLASSIC_ENEMY(element1) ||
5706 IS_CLASSIC_ENEMY(element2)) &&
5707 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5708 element1 != EL_FLAMES && element2 != EL_FLAMES)
5710 ResetGfxAnimation(x, y);
5711 GfxAction[x][y] = ACTION_ATTACKING;
5713 if (IS_PLAYER(x, y))
5714 DrawPlayerField(x, y);
5716 DrawLevelField(x, y);
5718 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5720 MovDelay[x][y] = 50;
5724 RemoveField(newx, newy);
5726 Feld[newx][newy] = EL_FLAMES;
5727 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5730 RemoveField(newx1, newy1);
5732 Feld[newx1][newy1] = EL_FLAMES;
5734 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5737 RemoveField(newx2, newy2);
5739 Feld[newx2][newy2] = EL_FLAMES;
5746 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5747 Feld[newx][newy] == EL_DIAMOND)
5749 if (IS_MOVING(newx, newy))
5750 RemoveMovingField(newx, newy);
5753 Feld[newx][newy] = EL_EMPTY;
5754 DrawLevelField(newx, newy);
5757 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5759 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5760 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5762 if (AmoebaNr[newx][newy])
5764 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5765 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5766 Feld[newx][newy] == EL_BD_AMOEBA)
5767 AmoebaCnt[AmoebaNr[newx][newy]]--;
5772 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5774 RemoveMovingField(newx, newy);
5777 if (IS_MOVING(newx, newy))
5779 RemoveMovingField(newx, newy);
5784 Feld[newx][newy] = EL_EMPTY;
5785 DrawLevelField(newx, newy);
5788 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5790 else if ((element == EL_PACMAN || element == EL_MOLE)
5791 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5793 if (AmoebaNr[newx][newy])
5795 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5796 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5797 Feld[newx][newy] == EL_BD_AMOEBA)
5798 AmoebaCnt[AmoebaNr[newx][newy]]--;
5801 if (element == EL_MOLE)
5803 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5804 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5806 ResetGfxAnimation(x, y);
5807 GfxAction[x][y] = ACTION_DIGGING;
5808 DrawLevelField(x, y);
5810 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5812 return; /* wait for shrinking amoeba */
5814 else /* element == EL_PACMAN */
5816 Feld[newx][newy] = EL_EMPTY;
5817 DrawLevelField(newx, newy);
5818 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5821 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5822 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5823 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5825 /* wait for shrinking amoeba to completely disappear */
5828 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5830 /* object was running against a wall */
5835 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5836 if (move_pattern & MV_ANY_DIRECTION &&
5837 move_pattern == MovDir[x][y])
5839 int blocking_element =
5840 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5842 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5845 element = Feld[x][y]; /* element might have changed */
5849 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5850 DrawLevelElementAnimation(x, y, element);
5852 if (DONT_TOUCH(element))
5853 TestIfBadThingTouchesPlayer(x, y);
5858 InitMovingField(x, y, MovDir[x][y]);
5860 PlayLevelSoundAction(x, y, ACTION_MOVING);
5864 ContinueMoving(x, y);
5867 void ContinueMoving(int x, int y)
5869 int element = Feld[x][y];
5870 struct ElementInfo *ei = &element_info[element];
5871 int direction = MovDir[x][y];
5872 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5873 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5874 int newx = x + dx, newy = y + dy;
5875 int stored = Store[x][y];
5876 int stored_new = Store[newx][newy];
5877 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5878 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5879 boolean last_line = (newy == lev_fieldy - 1);
5881 MovPos[x][y] += getElementMoveStepsize(x, y);
5883 if (pushed_by_player) /* special case: moving object pushed by player */
5884 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5886 if (ABS(MovPos[x][y]) < TILEX)
5888 DrawLevelField(x, y);
5890 return; /* element is still moving */
5893 /* element reached destination field */
5895 Feld[x][y] = EL_EMPTY;
5896 Feld[newx][newy] = element;
5897 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5899 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
5901 element = Feld[newx][newy] = EL_ACID;
5903 else if (element == EL_MOLE)
5905 Feld[x][y] = EL_SAND;
5907 DrawLevelFieldCrumbledSandNeighbours(x, y);
5909 else if (element == EL_QUICKSAND_FILLING)
5911 element = Feld[newx][newy] = get_next_element(element);
5912 Store[newx][newy] = Store[x][y];
5914 else if (element == EL_QUICKSAND_EMPTYING)
5916 Feld[x][y] = get_next_element(element);
5917 element = Feld[newx][newy] = Store[x][y];
5919 else if (element == EL_MAGIC_WALL_FILLING)
5921 element = Feld[newx][newy] = get_next_element(element);
5922 if (!game.magic_wall_active)
5923 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5924 Store[newx][newy] = Store[x][y];
5926 else if (element == EL_MAGIC_WALL_EMPTYING)
5928 Feld[x][y] = get_next_element(element);
5929 if (!game.magic_wall_active)
5930 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5931 element = Feld[newx][newy] = Store[x][y];
5933 #if USE_NEW_CUSTOM_VALUE
5934 InitField(newx, newy, FALSE);
5937 else if (element == EL_BD_MAGIC_WALL_FILLING)
5939 element = Feld[newx][newy] = get_next_element(element);
5940 if (!game.magic_wall_active)
5941 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5942 Store[newx][newy] = Store[x][y];
5944 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5946 Feld[x][y] = get_next_element(element);
5947 if (!game.magic_wall_active)
5948 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5949 element = Feld[newx][newy] = Store[x][y];
5951 #if USE_NEW_CUSTOM_VALUE
5952 InitField(newx, newy, FALSE);
5955 else if (element == EL_AMOEBA_DROPPING)
5957 Feld[x][y] = get_next_element(element);
5958 element = Feld[newx][newy] = Store[x][y];
5960 else if (element == EL_SOKOBAN_OBJECT)
5963 Feld[x][y] = Back[x][y];
5965 if (Back[newx][newy])
5966 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5968 Back[x][y] = Back[newx][newy] = 0;
5971 Store[x][y] = EL_EMPTY;
5976 MovDelay[newx][newy] = 0;
5979 if (CAN_CHANGE_OR_HAS_ACTION(element))
5981 if (CAN_CHANGE(element))
5984 /* copy element change control values to new field */
5985 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5986 ChangePage[newx][newy] = ChangePage[x][y];
5987 ChangeCount[newx][newy] = ChangeCount[x][y];
5988 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5991 #if USE_NEW_CUSTOM_VALUE
5992 CustomValue[newx][newy] = CustomValue[x][y];
5998 #if USE_NEW_CUSTOM_VALUE
5999 CustomValue[newx][newy] = CustomValue[x][y];
6003 ChangeDelay[x][y] = 0;
6004 ChangePage[x][y] = -1;
6005 ChangeCount[x][y] = 0;
6006 ChangeEvent[x][y] = -1;
6008 #if USE_NEW_CUSTOM_VALUE
6009 CustomValue[x][y] = 0;
6012 /* copy animation control values to new field */
6013 GfxFrame[newx][newy] = GfxFrame[x][y];
6014 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6015 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6016 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6018 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6020 /* some elements can leave other elements behind after moving */
6021 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6022 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6023 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6025 int move_leave_element = ei->move_leave_element;
6028 /* this makes it possible to leave the removed element again */
6029 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6030 move_leave_element = stored;
6032 /* this makes it possible to leave the removed element again */
6033 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6034 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6035 move_leave_element = stored;
6038 Feld[x][y] = move_leave_element;
6040 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6041 MovDir[x][y] = direction;
6043 InitField(x, y, FALSE);
6045 if (GFX_CRUMBLED(Feld[x][y]))
6046 DrawLevelFieldCrumbledSandNeighbours(x, y);
6048 if (ELEM_IS_PLAYER(move_leave_element))
6049 RelocatePlayer(x, y, move_leave_element);
6052 /* do this after checking for left-behind element */
6053 ResetGfxAnimation(x, y); /* reset animation values for old field */
6055 if (!CAN_MOVE(element) ||
6056 (CAN_FALL(element) && direction == MV_DOWN &&
6057 (element == EL_SPRING ||
6058 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6059 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6060 GfxDir[x][y] = MovDir[newx][newy] = 0;
6062 DrawLevelField(x, y);
6063 DrawLevelField(newx, newy);
6065 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6067 /* prevent pushed element from moving on in pushed direction */
6068 if (pushed_by_player && CAN_MOVE(element) &&
6069 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6070 !(element_info[element].move_pattern & direction))
6071 TurnRound(newx, newy);
6073 /* prevent elements on conveyor belt from moving on in last direction */
6074 if (pushed_by_conveyor && CAN_FALL(element) &&
6075 direction & MV_HORIZONTAL)
6076 MovDir[newx][newy] = 0;
6078 if (!pushed_by_player)
6080 int nextx = newx + dx, nexty = newy + dy;
6081 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6083 WasJustMoving[newx][newy] = 3;
6085 if (CAN_FALL(element) && direction == MV_DOWN)
6086 WasJustFalling[newx][newy] = 3;
6088 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6089 CheckCollision[newx][newy] = 2;
6092 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6094 TestIfBadThingTouchesPlayer(newx, newy);
6095 TestIfBadThingTouchesFriend(newx, newy);
6097 if (!IS_CUSTOM_ELEMENT(element))
6098 TestIfBadThingTouchesOtherBadThing(newx, newy);
6100 else if (element == EL_PENGUIN)
6101 TestIfFriendTouchesBadThing(newx, newy);
6103 /* give the player one last chance (one more frame) to move away */
6104 if (CAN_FALL(element) && direction == MV_DOWN &&
6105 (last_line || (!IS_FREE(x, newy + 1) &&
6106 (!IS_PLAYER(x, newy + 1) ||
6107 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6110 if (pushed_by_player && !game.use_change_when_pushing_bug)
6112 int push_side = MV_DIR_OPPOSITE(direction);
6113 struct PlayerInfo *player = PLAYERINFO(x, y);
6115 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6116 player->index_bit, push_side);
6117 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6118 player->index_bit, push_side);
6121 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6123 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6125 TestIfElementHitsCustomElement(newx, newy, direction);
6126 TestIfPlayerTouchesCustomElement(newx, newy);
6127 TestIfElementTouchesCustomElement(newx, newy);
6130 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6131 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6132 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6133 MV_DIR_OPPOSITE(direction));
6137 int AmoebeNachbarNr(int ax, int ay)
6140 int element = Feld[ax][ay];
6142 static int xy[4][2] =
6150 for (i = 0; i < NUM_DIRECTIONS; i++)
6152 int x = ax + xy[i][0];
6153 int y = ay + xy[i][1];
6155 if (!IN_LEV_FIELD(x, y))
6158 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6159 group_nr = AmoebaNr[x][y];
6165 void AmoebenVereinigen(int ax, int ay)
6167 int i, x, y, xx, yy;
6168 int new_group_nr = AmoebaNr[ax][ay];
6169 static int xy[4][2] =
6177 if (new_group_nr == 0)
6180 for (i = 0; i < NUM_DIRECTIONS; i++)
6185 if (!IN_LEV_FIELD(x, y))
6188 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6189 Feld[x][y] == EL_BD_AMOEBA ||
6190 Feld[x][y] == EL_AMOEBA_DEAD) &&
6191 AmoebaNr[x][y] != new_group_nr)
6193 int old_group_nr = AmoebaNr[x][y];
6195 if (old_group_nr == 0)
6198 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6199 AmoebaCnt[old_group_nr] = 0;
6200 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6201 AmoebaCnt2[old_group_nr] = 0;
6204 SCAN_PLAYFIELD(xx, yy)
6206 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6209 if (AmoebaNr[xx][yy] == old_group_nr)
6210 AmoebaNr[xx][yy] = new_group_nr;
6216 void AmoebeUmwandeln(int ax, int ay)
6220 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6222 int group_nr = AmoebaNr[ax][ay];
6227 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6228 printf("AmoebeUmwandeln(): This should never happen!\n");
6234 SCAN_PLAYFIELD(x, y)
6236 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6239 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6242 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6246 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6247 SND_AMOEBA_TURNING_TO_GEM :
6248 SND_AMOEBA_TURNING_TO_ROCK));
6253 static int xy[4][2] =
6261 for (i = 0; i < NUM_DIRECTIONS; i++)
6266 if (!IN_LEV_FIELD(x, y))
6269 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6271 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6272 SND_AMOEBA_TURNING_TO_GEM :
6273 SND_AMOEBA_TURNING_TO_ROCK));
6280 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6283 int group_nr = AmoebaNr[ax][ay];
6284 boolean done = FALSE;
6289 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6290 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6296 SCAN_PLAYFIELD(x, y)
6298 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6301 if (AmoebaNr[x][y] == group_nr &&
6302 (Feld[x][y] == EL_AMOEBA_DEAD ||
6303 Feld[x][y] == EL_BD_AMOEBA ||
6304 Feld[x][y] == EL_AMOEBA_GROWING))
6307 Feld[x][y] = new_element;
6308 InitField(x, y, FALSE);
6309 DrawLevelField(x, y);
6315 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6316 SND_BD_AMOEBA_TURNING_TO_ROCK :
6317 SND_BD_AMOEBA_TURNING_TO_GEM));
6320 void AmoebeWaechst(int x, int y)
6322 static unsigned long sound_delay = 0;
6323 static unsigned long sound_delay_value = 0;
6325 if (!MovDelay[x][y]) /* start new growing cycle */
6329 if (DelayReached(&sound_delay, sound_delay_value))
6331 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6332 sound_delay_value = 30;
6336 if (MovDelay[x][y]) /* wait some time before growing bigger */
6339 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6341 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6342 6 - MovDelay[x][y]);
6344 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6347 if (!MovDelay[x][y])
6349 Feld[x][y] = Store[x][y];
6351 DrawLevelField(x, y);
6356 void AmoebaDisappearing(int x, int y)
6358 static unsigned long sound_delay = 0;
6359 static unsigned long sound_delay_value = 0;
6361 if (!MovDelay[x][y]) /* start new shrinking cycle */
6365 if (DelayReached(&sound_delay, sound_delay_value))
6366 sound_delay_value = 30;
6369 if (MovDelay[x][y]) /* wait some time before shrinking */
6372 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6374 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6375 6 - MovDelay[x][y]);
6377 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6380 if (!MovDelay[x][y])
6382 Feld[x][y] = EL_EMPTY;
6383 DrawLevelField(x, y);
6385 /* don't let mole enter this field in this cycle;
6386 (give priority to objects falling to this field from above) */
6392 void AmoebeAbleger(int ax, int ay)
6395 int element = Feld[ax][ay];
6396 int graphic = el2img(element);
6397 int newax = ax, neway = ay;
6398 static int xy[4][2] =
6406 if (!level.amoeba_speed)
6408 Feld[ax][ay] = EL_AMOEBA_DEAD;
6409 DrawLevelField(ax, ay);
6413 if (IS_ANIMATED(graphic))
6414 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6416 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6417 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6419 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6422 if (MovDelay[ax][ay])
6426 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6429 int x = ax + xy[start][0];
6430 int y = ay + xy[start][1];
6432 if (!IN_LEV_FIELD(x, y))
6435 if (IS_FREE(x, y) ||
6436 CAN_GROW_INTO(Feld[x][y]) ||
6437 Feld[x][y] == EL_QUICKSAND_EMPTY)
6443 if (newax == ax && neway == ay)
6446 else /* normal or "filled" (BD style) amoeba */
6449 boolean waiting_for_player = FALSE;
6451 for (i = 0; i < NUM_DIRECTIONS; i++)
6453 int j = (start + i) % 4;
6454 int x = ax + xy[j][0];
6455 int y = ay + xy[j][1];
6457 if (!IN_LEV_FIELD(x, y))
6460 if (IS_FREE(x, y) ||
6461 CAN_GROW_INTO(Feld[x][y]) ||
6462 Feld[x][y] == EL_QUICKSAND_EMPTY)
6468 else if (IS_PLAYER(x, y))
6469 waiting_for_player = TRUE;
6472 if (newax == ax && neway == ay) /* amoeba cannot grow */
6474 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6476 Feld[ax][ay] = EL_AMOEBA_DEAD;
6477 DrawLevelField(ax, ay);
6478 AmoebaCnt[AmoebaNr[ax][ay]]--;
6480 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6482 if (element == EL_AMOEBA_FULL)
6483 AmoebeUmwandeln(ax, ay);
6484 else if (element == EL_BD_AMOEBA)
6485 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6490 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6492 /* amoeba gets larger by growing in some direction */
6494 int new_group_nr = AmoebaNr[ax][ay];
6497 if (new_group_nr == 0)
6499 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6500 printf("AmoebeAbleger(): This should never happen!\n");
6505 AmoebaNr[newax][neway] = new_group_nr;
6506 AmoebaCnt[new_group_nr]++;
6507 AmoebaCnt2[new_group_nr]++;
6509 /* if amoeba touches other amoeba(s) after growing, unify them */
6510 AmoebenVereinigen(newax, neway);
6512 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6514 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6520 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6521 (neway == lev_fieldy - 1 && newax != ax))
6523 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6524 Store[newax][neway] = element;
6526 else if (neway == ay)
6528 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6530 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6534 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6535 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6536 Store[ax][ay] = EL_AMOEBA_DROP;
6537 ContinueMoving(ax, ay);
6541 DrawLevelField(newax, neway);
6544 void Life(int ax, int ay)
6548 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6551 int element = Feld[ax][ay];
6552 int graphic = el2img(element);
6553 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6555 boolean changed = FALSE;
6557 if (IS_ANIMATED(graphic))
6558 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6563 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6564 MovDelay[ax][ay] = life_time;
6566 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6569 if (MovDelay[ax][ay])
6573 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6575 int xx = ax+x1, yy = ay+y1;
6578 if (!IN_LEV_FIELD(xx, yy))
6581 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6583 int x = xx+x2, y = yy+y2;
6585 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6588 if (((Feld[x][y] == element ||
6589 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6591 (IS_FREE(x, y) && Stop[x][y]))
6595 if (xx == ax && yy == ay) /* field in the middle */
6597 if (nachbarn < life_parameter[0] ||
6598 nachbarn > life_parameter[1])
6600 Feld[xx][yy] = EL_EMPTY;
6602 DrawLevelField(xx, yy);
6603 Stop[xx][yy] = TRUE;
6607 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6608 { /* free border field */
6609 if (nachbarn >= life_parameter[2] &&
6610 nachbarn <= life_parameter[3])
6612 Feld[xx][yy] = element;
6613 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6615 DrawLevelField(xx, yy);
6616 Stop[xx][yy] = TRUE;
6623 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6624 SND_GAME_OF_LIFE_GROWING);
6627 static void InitRobotWheel(int x, int y)
6629 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6632 static void RunRobotWheel(int x, int y)
6634 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6637 static void StopRobotWheel(int x, int y)
6639 if (ZX == x && ZY == y)
6643 static void InitTimegateWheel(int x, int y)
6645 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6648 static void RunTimegateWheel(int x, int y)
6650 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6653 static void InitMagicBallDelay(int x, int y)
6656 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
6658 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
6662 static void ActivateMagicBall(int bx, int by)
6666 if (level.ball_random)
6668 int pos_border = RND(8); /* select one of the eight border elements */
6669 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
6670 int xx = pos_content % 3;
6671 int yy = pos_content / 3;
6676 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6677 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6681 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
6683 int xx = x - bx + 1;
6684 int yy = y - by + 1;
6686 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6687 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6691 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
6694 void CheckExit(int x, int y)
6696 if (local_player->gems_still_needed > 0 ||
6697 local_player->sokobanfields_still_needed > 0 ||
6698 local_player->lights_still_needed > 0)
6700 int element = Feld[x][y];
6701 int graphic = el2img(element);
6703 if (IS_ANIMATED(graphic))
6704 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6709 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6712 Feld[x][y] = EL_EXIT_OPENING;
6714 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6717 void CheckExitSP(int x, int y)
6719 if (local_player->gems_still_needed > 0)
6721 int element = Feld[x][y];
6722 int graphic = el2img(element);
6724 if (IS_ANIMATED(graphic))
6725 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6730 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6733 Feld[x][y] = EL_SP_EXIT_OPENING;
6735 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6738 static void CloseAllOpenTimegates()
6743 SCAN_PLAYFIELD(x, y)
6745 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6748 int element = Feld[x][y];
6750 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6752 Feld[x][y] = EL_TIMEGATE_CLOSING;
6754 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6759 void EdelsteinFunkeln(int x, int y)
6761 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6764 if (Feld[x][y] == EL_BD_DIAMOND)
6767 if (MovDelay[x][y] == 0) /* next animation frame */
6768 MovDelay[x][y] = 11 * !SimpleRND(500);
6770 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6774 if (setup.direct_draw && MovDelay[x][y])
6775 SetDrawtoField(DRAW_BUFFERED);
6777 DrawLevelElementAnimation(x, y, Feld[x][y]);
6779 if (MovDelay[x][y] != 0)
6781 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6782 10 - MovDelay[x][y]);
6784 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6786 if (setup.direct_draw)
6790 dest_x = FX + SCREENX(x) * TILEX;
6791 dest_y = FY + SCREENY(y) * TILEY;
6793 BlitBitmap(drawto_field, window,
6794 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6795 SetDrawtoField(DRAW_DIRECT);
6801 void MauerWaechst(int x, int y)
6805 if (!MovDelay[x][y]) /* next animation frame */
6806 MovDelay[x][y] = 3 * delay;
6808 if (MovDelay[x][y]) /* wait some time before next frame */
6812 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6814 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6815 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6817 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6820 if (!MovDelay[x][y])
6822 if (MovDir[x][y] == MV_LEFT)
6824 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6825 DrawLevelField(x - 1, y);
6827 else if (MovDir[x][y] == MV_RIGHT)
6829 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6830 DrawLevelField(x + 1, y);
6832 else if (MovDir[x][y] == MV_UP)
6834 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6835 DrawLevelField(x, y - 1);
6839 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6840 DrawLevelField(x, y + 1);
6843 Feld[x][y] = Store[x][y];
6845 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6846 DrawLevelField(x, y);
6851 void MauerAbleger(int ax, int ay)
6853 int element = Feld[ax][ay];
6854 int graphic = el2img(element);
6855 boolean oben_frei = FALSE, unten_frei = FALSE;
6856 boolean links_frei = FALSE, rechts_frei = FALSE;
6857 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6858 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6859 boolean new_wall = FALSE;
6861 if (IS_ANIMATED(graphic))
6862 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6864 if (!MovDelay[ax][ay]) /* start building new wall */
6865 MovDelay[ax][ay] = 6;
6867 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6870 if (MovDelay[ax][ay])
6874 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6876 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6878 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6880 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6883 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6884 element == EL_EXPANDABLE_WALL_ANY)
6888 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6889 Store[ax][ay-1] = element;
6890 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6891 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6892 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6893 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6898 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6899 Store[ax][ay+1] = element;
6900 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6901 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6902 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6903 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6908 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6909 element == EL_EXPANDABLE_WALL_ANY ||
6910 element == EL_EXPANDABLE_WALL)
6914 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6915 Store[ax-1][ay] = element;
6916 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6917 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6918 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6919 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6925 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6926 Store[ax+1][ay] = element;
6927 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6928 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6929 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6930 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6935 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6936 DrawLevelField(ax, ay);
6938 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6940 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6941 unten_massiv = TRUE;
6942 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6943 links_massiv = TRUE;
6944 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6945 rechts_massiv = TRUE;
6947 if (((oben_massiv && unten_massiv) ||
6948 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6949 element == EL_EXPANDABLE_WALL) &&
6950 ((links_massiv && rechts_massiv) ||
6951 element == EL_EXPANDABLE_WALL_VERTICAL))
6952 Feld[ax][ay] = EL_WALL;
6955 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6958 void CheckForDragon(int x, int y)
6961 boolean dragon_found = FALSE;
6962 static int xy[4][2] =
6970 for (i = 0; i < NUM_DIRECTIONS; i++)
6972 for (j = 0; j < 4; j++)
6974 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6976 if (IN_LEV_FIELD(xx, yy) &&
6977 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6979 if (Feld[xx][yy] == EL_DRAGON)
6980 dragon_found = TRUE;
6989 for (i = 0; i < NUM_DIRECTIONS; i++)
6991 for (j = 0; j < 3; j++)
6993 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6995 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6997 Feld[xx][yy] = EL_EMPTY;
6998 DrawLevelField(xx, yy);
7007 static void InitBuggyBase(int x, int y)
7009 int element = Feld[x][y];
7010 int activating_delay = FRAMES_PER_SECOND / 4;
7013 (element == EL_SP_BUGGY_BASE ?
7014 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7015 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7017 element == EL_SP_BUGGY_BASE_ACTIVE ?
7018 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7021 static void WarnBuggyBase(int x, int y)
7024 static int xy[4][2] =
7032 for (i = 0; i < NUM_DIRECTIONS; i++)
7034 int xx = x + xy[i][0], yy = y + xy[i][1];
7036 if (IS_PLAYER(xx, yy))
7038 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7045 static void InitTrap(int x, int y)
7047 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7050 static void ActivateTrap(int x, int y)
7052 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7055 static void ChangeActiveTrap(int x, int y)
7057 int graphic = IMG_TRAP_ACTIVE;
7059 /* if new animation frame was drawn, correct crumbled sand border */
7060 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7061 DrawLevelFieldCrumbledSand(x, y);
7064 static int getSpecialActionElement(int element, int number, int base_element)
7066 return (element != EL_EMPTY ? element :
7067 number != -1 ? base_element + number - 1 :
7071 static int getModifiedActionNumber(int value_old, int operator, int operand,
7072 int value_min, int value_max)
7074 int value_new = (operator == CA_MODE_SET ? operand :
7075 operator == CA_MODE_ADD ? value_old + operand :
7076 operator == CA_MODE_SUBTRACT ? value_old - operand :
7077 operator == CA_MODE_MULTIPLY ? value_old * operand :
7078 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7079 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7082 return (value_new < value_min ? value_min :
7083 value_new > value_max ? value_max :
7087 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7089 struct ElementInfo *ei = &element_info[element];
7090 struct ElementChangeInfo *change = &ei->change_page[page];
7091 int action_type = change->action_type;
7092 int action_mode = change->action_mode;
7093 int action_arg = change->action_arg;
7096 if (!change->has_action)
7099 /* ---------- determine action paramater values -------------------------- */
7101 int level_time_value =
7102 (level.time > 0 ? TimeLeft :
7105 int action_arg_element =
7106 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7107 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7108 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7111 int action_arg_direction =
7112 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7113 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7114 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7115 change->actual_trigger_side :
7116 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7117 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7120 int action_arg_number_min =
7121 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7124 int action_arg_number_max =
7125 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7126 action_type == CA_SET_LEVEL_GEMS ? 999 :
7127 action_type == CA_SET_LEVEL_TIME ? 9999 :
7128 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7129 action_type == CA_SET_CE_SCORE ? 9999 :
7130 action_type == CA_SET_CE_VALUE ? 9999 :
7133 int action_arg_number_reset =
7134 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7135 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7136 action_type == CA_SET_LEVEL_TIME ? level.time :
7137 action_type == CA_SET_LEVEL_SCORE ? 0 :
7138 action_type == CA_SET_CE_SCORE ? 0 :
7140 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7142 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7146 int action_arg_number =
7147 (action_arg <= CA_ARG_MAX ? action_arg :
7148 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7149 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7150 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7151 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7152 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7153 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7154 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7155 #if USE_NEW_CUSTOM_VALUE
7156 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7158 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7160 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7161 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7162 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7163 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7164 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7165 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
7168 int action_arg_number_old =
7169 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7170 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7171 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7172 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7173 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7176 int action_arg_number_new =
7177 getModifiedActionNumber(action_arg_number_old,
7178 action_mode, action_arg_number,
7179 action_arg_number_min, action_arg_number_max);
7181 int trigger_player_bits =
7182 (change->actual_trigger_player >= EL_PLAYER_1 &&
7183 change->actual_trigger_player <= EL_PLAYER_4 ?
7184 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7187 int action_arg_player_bits =
7188 (action_arg >= CA_ARG_PLAYER_1 &&
7189 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7190 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7193 /* ---------- execute action -------------------------------------------- */
7202 /* ---------- level actions ------------------------------------------- */
7204 case CA_RESTART_LEVEL:
7206 game.restart_level = TRUE;
7211 case CA_SHOW_ENVELOPE:
7213 int element = getSpecialActionElement(action_arg_element,
7214 action_arg_number, EL_ENVELOPE_1);
7216 if (IS_ENVELOPE(element))
7217 local_player->show_envelope = element;
7222 case CA_SET_LEVEL_TIME:
7224 if (level.time > 0) /* only modify limited time value */
7226 TimeLeft = action_arg_number_new;
7228 DrawGameValue_Time(TimeLeft);
7230 if (!TimeLeft && setup.time_limit)
7231 for (i = 0; i < MAX_PLAYERS; i++)
7232 KillPlayer(&stored_player[i]);
7238 case CA_SET_LEVEL_SCORE:
7240 local_player->score = action_arg_number_new;
7242 DrawGameValue_Score(local_player->score);
7247 case CA_SET_LEVEL_GEMS:
7249 local_player->gems_still_needed = action_arg_number_new;
7251 DrawGameValue_Emeralds(local_player->gems_still_needed);
7256 case CA_SET_LEVEL_GRAVITY:
7258 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7259 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7260 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7265 case CA_SET_LEVEL_WIND:
7267 game.wind_direction = action_arg_direction;
7272 /* ---------- player actions ------------------------------------------ */
7274 case CA_MOVE_PLAYER:
7276 /* automatically move to the next field in specified direction */
7277 for (i = 0; i < MAX_PLAYERS; i++)
7278 if (trigger_player_bits & (1 << i))
7279 stored_player[i].programmed_action = action_arg_direction;
7284 case CA_EXIT_PLAYER:
7286 for (i = 0; i < MAX_PLAYERS; i++)
7287 if (action_arg_player_bits & (1 << i))
7288 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7293 case CA_KILL_PLAYER:
7295 for (i = 0; i < MAX_PLAYERS; i++)
7296 if (action_arg_player_bits & (1 << i))
7297 KillPlayer(&stored_player[i]);
7302 case CA_SET_PLAYER_KEYS:
7304 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7305 int element = getSpecialActionElement(action_arg_element,
7306 action_arg_number, EL_KEY_1);
7308 if (IS_KEY(element))
7310 for (i = 0; i < MAX_PLAYERS; i++)
7312 if (trigger_player_bits & (1 << i))
7314 stored_player[i].key[KEY_NR(element)] = key_state;
7316 DrawGameValue_Keys(stored_player[i].key);
7318 redraw_mask |= REDRAW_DOOR_1;
7326 case CA_SET_PLAYER_SPEED:
7328 for (i = 0; i < MAX_PLAYERS; i++)
7330 if (trigger_player_bits & (1 << i))
7332 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7334 if (action_arg == CA_ARG_SPEED_FASTER &&
7335 stored_player[i].cannot_move)
7337 action_arg_number = STEPSIZE_VERY_SLOW;
7339 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7340 action_arg == CA_ARG_SPEED_FASTER)
7342 action_arg_number = 2;
7343 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7348 getModifiedActionNumber(move_stepsize,
7351 action_arg_number_min,
7352 action_arg_number_max);
7355 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7357 /* make sure that value is power of 2 */
7358 move_stepsize = (1 << log_2(move_stepsize));
7360 /* do no immediately change -- the player might just be moving */
7361 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7363 stored_player[i].cannot_move =
7364 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7372 case CA_SET_PLAYER_SHIELD:
7374 for (i = 0; i < MAX_PLAYERS; i++)
7376 if (trigger_player_bits & (1 << i))
7378 if (action_arg == CA_ARG_SHIELD_OFF)
7380 stored_player[i].shield_normal_time_left = 0;
7381 stored_player[i].shield_deadly_time_left = 0;
7383 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7385 stored_player[i].shield_normal_time_left = 999999;
7387 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7389 stored_player[i].shield_normal_time_left = 999999;
7390 stored_player[i].shield_deadly_time_left = 999999;
7398 case CA_SET_PLAYER_ARTWORK:
7400 for (i = 0; i < MAX_PLAYERS; i++)
7402 if (trigger_player_bits & (1 << i))
7404 int artwork_element = action_arg_element;
7406 if (action_arg == CA_ARG_ELEMENT_RESET)
7408 (level.use_artwork_element[i] ? level.artwork_element[i] :
7409 stored_player[i].element_nr);
7411 stored_player[i].artwork_element = artwork_element;
7413 SetPlayerWaiting(&stored_player[i], FALSE);
7415 /* set number of special actions for bored and sleeping animation */
7416 stored_player[i].num_special_action_bored =
7417 get_num_special_action(artwork_element,
7418 ACTION_BORING_1, ACTION_BORING_LAST);
7419 stored_player[i].num_special_action_sleeping =
7420 get_num_special_action(artwork_element,
7421 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7428 /* ---------- CE actions ---------------------------------------------- */
7430 case CA_SET_CE_SCORE:
7432 ei->collect_score = action_arg_number_new;
7437 case CA_SET_CE_VALUE:
7439 #if USE_NEW_CUSTOM_VALUE
7440 int last_custom_value = CustomValue[x][y];
7442 CustomValue[x][y] = action_arg_number_new;
7445 printf("::: Count == %d\n", CustomValue[x][y]);
7448 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7451 printf("::: CE_VALUE_GETS_ZERO\n");
7454 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7455 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7462 /* ---------- engine actions ------------------------------------------ */
7464 case CA_SET_ENGINE_SCAN_MODE:
7466 InitPlayfieldScanMode(action_arg);
7476 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7478 int previous_move_direction = MovDir[x][y];
7479 #if USE_NEW_CUSTOM_VALUE
7480 int last_ce_value = CustomValue[x][y];
7482 boolean add_player = (ELEM_IS_PLAYER(element) &&
7483 IS_WALKABLE(Feld[x][y]));
7485 /* check if element under player changes from accessible to unaccessible
7486 (needed for special case of dropping element which then changes) */
7487 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7488 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7497 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7498 RemoveMovingField(x, y);
7502 Feld[x][y] = element;
7504 ResetGfxAnimation(x, y);
7505 ResetRandomAnimationValue(x, y);
7507 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7508 MovDir[x][y] = previous_move_direction;
7510 #if USE_NEW_CUSTOM_VALUE
7511 if (element_info[Feld[x][y]].use_last_ce_value)
7512 CustomValue[x][y] = last_ce_value;
7515 InitField_WithBug1(x, y, FALSE);
7517 DrawLevelField(x, y);
7519 if (GFX_CRUMBLED(Feld[x][y]))
7520 DrawLevelFieldCrumbledSandNeighbours(x, y);
7523 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7524 if (ELEM_IS_PLAYER(element))
7525 RelocatePlayer(x, y, element);
7528 ChangeCount[x][y]++; /* count number of changes in the same frame */
7530 TestIfBadThingTouchesPlayer(x, y);
7531 TestIfPlayerTouchesCustomElement(x, y);
7532 TestIfElementTouchesCustomElement(x, y);
7535 static void CreateField(int x, int y, int element)
7537 CreateFieldExt(x, y, element, FALSE);
7540 static void CreateElementFromChange(int x, int y, int element)
7542 CreateFieldExt(x, y, element, TRUE);
7545 static boolean ChangeElement(int x, int y, int element, int page)
7547 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7549 int old_element = Feld[x][y];
7551 /* always use default change event to prevent running into a loop */
7552 if (ChangeEvent[x][y] == -1)
7553 ChangeEvent[x][y] = CE_DELAY;
7555 if (ChangeEvent[x][y] == CE_DELAY)
7557 /* reset actual trigger element, trigger player and action element */
7558 change->actual_trigger_element = EL_EMPTY;
7559 change->actual_trigger_player = EL_PLAYER_1;
7560 change->actual_trigger_side = CH_SIDE_NONE;
7561 change->actual_trigger_ce_value = 0;
7564 /* do not change elements more than a specified maximum number of changes */
7565 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7568 ChangeCount[x][y]++; /* count number of changes in the same frame */
7570 if (change->explode)
7577 if (change->use_target_content)
7579 boolean complete_replace = TRUE;
7580 boolean can_replace[3][3];
7583 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7586 boolean is_walkable;
7587 boolean is_diggable;
7588 boolean is_collectible;
7589 boolean is_removable;
7590 boolean is_destructible;
7591 int ex = x + xx - 1;
7592 int ey = y + yy - 1;
7593 int content_element = change->target_content.e[xx][yy];
7596 can_replace[xx][yy] = TRUE;
7598 if (ex == x && ey == y) /* do not check changing element itself */
7601 if (content_element == EL_EMPTY_SPACE)
7603 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7608 if (!IN_LEV_FIELD(ex, ey))
7610 can_replace[xx][yy] = FALSE;
7611 complete_replace = FALSE;
7618 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7619 e = MovingOrBlocked2Element(ex, ey);
7621 is_empty = (IS_FREE(ex, ey) ||
7622 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7624 is_walkable = (is_empty || IS_WALKABLE(e));
7625 is_diggable = (is_empty || IS_DIGGABLE(e));
7626 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7627 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7628 is_removable = (is_diggable || is_collectible);
7630 can_replace[xx][yy] =
7631 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7632 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7633 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7634 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7635 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7636 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7637 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7639 if (!can_replace[xx][yy])
7640 complete_replace = FALSE;
7643 if (!change->only_if_complete || complete_replace)
7645 boolean something_has_changed = FALSE;
7647 if (change->only_if_complete && change->use_random_replace &&
7648 RND(100) < change->random_percentage)
7651 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7653 int ex = x + xx - 1;
7654 int ey = y + yy - 1;
7655 int content_element;
7657 if (can_replace[xx][yy] && (!change->use_random_replace ||
7658 RND(100) < change->random_percentage))
7660 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7661 RemoveMovingField(ex, ey);
7663 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7665 content_element = change->target_content.e[xx][yy];
7666 target_element = GET_TARGET_ELEMENT(content_element, change);
7668 CreateElementFromChange(ex, ey, target_element);
7670 something_has_changed = TRUE;
7672 /* for symmetry reasons, freeze newly created border elements */
7673 if (ex != x || ey != y)
7674 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7678 if (something_has_changed)
7680 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7681 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7687 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7689 CreateElementFromChange(x, y, target_element);
7691 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7692 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7695 /* this uses direct change before indirect change */
7696 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7701 #if USE_NEW_DELAYED_ACTION
7703 static void HandleElementChange(int x, int y, int page)
7705 int element = MovingOrBlocked2Element(x, y);
7706 struct ElementInfo *ei = &element_info[element];
7707 struct ElementChangeInfo *change = &ei->change_page[page];
7710 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7711 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7714 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7715 x, y, element, element_info[element].token_name);
7716 printf("HandleElementChange(): This should never happen!\n");
7721 /* this can happen with classic bombs on walkable, changing elements */
7722 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7725 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7726 ChangeDelay[x][y] = 0;
7732 if (ChangeDelay[x][y] == 0) /* initialize element change */
7734 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7736 if (change->can_change)
7738 ResetGfxAnimation(x, y);
7739 ResetRandomAnimationValue(x, y);
7741 if (change->pre_change_function)
7742 change->pre_change_function(x, y);
7746 ChangeDelay[x][y]--;
7748 if (ChangeDelay[x][y] != 0) /* continue element change */
7750 if (change->can_change)
7752 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7754 if (IS_ANIMATED(graphic))
7755 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7757 if (change->change_function)
7758 change->change_function(x, y);
7761 else /* finish element change */
7763 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7765 page = ChangePage[x][y];
7766 ChangePage[x][y] = -1;
7768 change = &ei->change_page[page];
7771 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7773 ChangeDelay[x][y] = 1; /* try change after next move step */
7774 ChangePage[x][y] = page; /* remember page to use for change */
7779 if (change->can_change)
7781 if (ChangeElement(x, y, element, page))
7783 if (change->post_change_function)
7784 change->post_change_function(x, y);
7788 if (change->has_action)
7789 ExecuteCustomElementAction(x, y, element, page);
7795 static void HandleElementChange(int x, int y, int page)
7797 int element = MovingOrBlocked2Element(x, y);
7798 struct ElementInfo *ei = &element_info[element];
7799 struct ElementChangeInfo *change = &ei->change_page[page];
7802 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7805 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7806 x, y, element, element_info[element].token_name);
7807 printf("HandleElementChange(): This should never happen!\n");
7812 /* this can happen with classic bombs on walkable, changing elements */
7813 if (!CAN_CHANGE(element))
7816 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7817 ChangeDelay[x][y] = 0;
7823 if (ChangeDelay[x][y] == 0) /* initialize element change */
7825 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7827 ResetGfxAnimation(x, y);
7828 ResetRandomAnimationValue(x, y);
7830 if (change->pre_change_function)
7831 change->pre_change_function(x, y);
7834 ChangeDelay[x][y]--;
7836 if (ChangeDelay[x][y] != 0) /* continue element change */
7838 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7840 if (IS_ANIMATED(graphic))
7841 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7843 if (change->change_function)
7844 change->change_function(x, y);
7846 else /* finish element change */
7848 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7850 page = ChangePage[x][y];
7851 ChangePage[x][y] = -1;
7853 change = &ei->change_page[page];
7856 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7858 ChangeDelay[x][y] = 1; /* try change after next move step */
7859 ChangePage[x][y] = page; /* remember page to use for change */
7864 if (ChangeElement(x, y, element, page))
7866 if (change->post_change_function)
7867 change->post_change_function(x, y);
7874 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
7875 int trigger_element,
7881 boolean change_done_any = FALSE;
7882 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7885 if (!(trigger_events[trigger_element][trigger_event]))
7888 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7890 int element = EL_CUSTOM_START + i;
7891 boolean change_done = FALSE;
7894 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7895 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7898 for (p = 0; p < element_info[element].num_change_pages; p++)
7900 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7902 if (change->can_change_or_has_action &&
7903 change->has_event[trigger_event] &&
7904 change->trigger_side & trigger_side &&
7905 change->trigger_player & trigger_player &&
7906 change->trigger_page & trigger_page_bits &&
7907 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7909 change->actual_trigger_element = trigger_element;
7910 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7911 change->actual_trigger_side = trigger_side;
7912 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
7914 if ((change->can_change && !change_done) || change->has_action)
7919 SCAN_PLAYFIELD(x, y)
7921 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7924 if (Feld[x][y] == element)
7926 if (change->can_change && !change_done)
7928 ChangeDelay[x][y] = 1;
7929 ChangeEvent[x][y] = trigger_event;
7931 HandleElementChange(x, y, p);
7933 #if USE_NEW_DELAYED_ACTION
7934 else if (change->has_action)
7936 ExecuteCustomElementAction(x, y, element, p);
7937 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7940 if (change->has_action)
7942 ExecuteCustomElementAction(x, y, element, p);
7943 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7949 if (change->can_change)
7952 change_done_any = TRUE;
7959 return change_done_any;
7962 static boolean CheckElementChangeExt(int x, int y,
7964 int trigger_element,
7969 boolean change_done = FALSE;
7972 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7973 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7976 if (Feld[x][y] == EL_BLOCKED)
7978 Blocked2Moving(x, y, &x, &y);
7979 element = Feld[x][y];
7982 if (Feld[x][y] != element) /* check if element has already changed */
7985 for (p = 0; p < element_info[element].num_change_pages; p++)
7987 struct ElementChangeInfo *change = &element_info[element].change_page[p];
7989 boolean check_trigger_element =
7990 (trigger_event == CE_TOUCHING_X ||
7991 trigger_event == CE_HITTING_X ||
7992 trigger_event == CE_HIT_BY_X);
7994 if (change->can_change_or_has_action &&
7995 change->has_event[trigger_event] &&
7996 change->trigger_side & trigger_side &&
7997 change->trigger_player & trigger_player &&
7998 (!check_trigger_element ||
7999 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8001 change->actual_trigger_element = trigger_element;
8002 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8003 change->actual_trigger_side = trigger_side;
8004 change->actual_trigger_ce_value = CustomValue[x][y];
8006 /* special case: trigger element not at (x,y) position for some events */
8007 if (check_trigger_element)
8019 { 0, 0 }, { 0, 0 }, { 0, 0 },
8023 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8024 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8026 change->actual_trigger_ce_value = CustomValue[xx][yy];
8029 if (change->can_change && !change_done)
8031 ChangeDelay[x][y] = 1;
8032 ChangeEvent[x][y] = trigger_event;
8034 HandleElementChange(x, y, p);
8038 #if USE_NEW_DELAYED_ACTION
8039 else if (change->has_action)
8041 ExecuteCustomElementAction(x, y, element, p);
8042 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8045 if (change->has_action)
8047 ExecuteCustomElementAction(x, y, element, p);
8048 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8057 static void PlayPlayerSound(struct PlayerInfo *player)
8059 int jx = player->jx, jy = player->jy;
8060 int sound_element = player->artwork_element;
8061 int last_action = player->last_action_waiting;
8062 int action = player->action_waiting;
8064 if (player->is_waiting)
8066 if (action != last_action)
8067 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8069 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8073 if (action != last_action)
8074 StopSound(element_info[sound_element].sound[last_action]);
8076 if (last_action == ACTION_SLEEPING)
8077 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8081 static void PlayAllPlayersSound()
8085 for (i = 0; i < MAX_PLAYERS; i++)
8086 if (stored_player[i].active)
8087 PlayPlayerSound(&stored_player[i]);
8090 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8092 boolean last_waiting = player->is_waiting;
8093 int move_dir = player->MovDir;
8095 player->last_action_waiting = player->action_waiting;
8099 if (!last_waiting) /* not waiting -> waiting */
8101 player->is_waiting = TRUE;
8103 player->frame_counter_bored =
8105 game.player_boring_delay_fixed +
8106 SimpleRND(game.player_boring_delay_random);
8107 player->frame_counter_sleeping =
8109 game.player_sleeping_delay_fixed +
8110 SimpleRND(game.player_sleeping_delay_random);
8112 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8115 if (game.player_sleeping_delay_fixed +
8116 game.player_sleeping_delay_random > 0 &&
8117 player->anim_delay_counter == 0 &&
8118 player->post_delay_counter == 0 &&
8119 FrameCounter >= player->frame_counter_sleeping)
8120 player->is_sleeping = TRUE;
8121 else if (game.player_boring_delay_fixed +
8122 game.player_boring_delay_random > 0 &&
8123 FrameCounter >= player->frame_counter_bored)
8124 player->is_bored = TRUE;
8126 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8127 player->is_bored ? ACTION_BORING :
8130 if (player->is_sleeping)
8132 if (player->num_special_action_sleeping > 0)
8134 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8136 int last_special_action = player->special_action_sleeping;
8137 int num_special_action = player->num_special_action_sleeping;
8138 int special_action =
8139 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8140 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8141 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8142 last_special_action + 1 : ACTION_SLEEPING);
8143 int special_graphic =
8144 el_act_dir2img(player->artwork_element, special_action, move_dir);
8146 player->anim_delay_counter =
8147 graphic_info[special_graphic].anim_delay_fixed +
8148 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8149 player->post_delay_counter =
8150 graphic_info[special_graphic].post_delay_fixed +
8151 SimpleRND(graphic_info[special_graphic].post_delay_random);
8153 player->special_action_sleeping = special_action;
8156 if (player->anim_delay_counter > 0)
8158 player->action_waiting = player->special_action_sleeping;
8159 player->anim_delay_counter--;
8161 else if (player->post_delay_counter > 0)
8163 player->post_delay_counter--;
8167 else if (player->is_bored)
8169 if (player->num_special_action_bored > 0)
8171 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8173 int special_action =
8174 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8175 int special_graphic =
8176 el_act_dir2img(player->artwork_element, special_action, move_dir);
8178 player->anim_delay_counter =
8179 graphic_info[special_graphic].anim_delay_fixed +
8180 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8181 player->post_delay_counter =
8182 graphic_info[special_graphic].post_delay_fixed +
8183 SimpleRND(graphic_info[special_graphic].post_delay_random);
8185 player->special_action_bored = special_action;
8188 if (player->anim_delay_counter > 0)
8190 player->action_waiting = player->special_action_bored;
8191 player->anim_delay_counter--;
8193 else if (player->post_delay_counter > 0)
8195 player->post_delay_counter--;
8200 else if (last_waiting) /* waiting -> not waiting */
8202 player->is_waiting = FALSE;
8203 player->is_bored = FALSE;
8204 player->is_sleeping = FALSE;
8206 player->frame_counter_bored = -1;
8207 player->frame_counter_sleeping = -1;
8209 player->anim_delay_counter = 0;
8210 player->post_delay_counter = 0;
8212 player->action_waiting = ACTION_DEFAULT;
8214 player->special_action_bored = ACTION_DEFAULT;
8215 player->special_action_sleeping = ACTION_DEFAULT;
8219 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8221 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8222 int left = player_action & JOY_LEFT;
8223 int right = player_action & JOY_RIGHT;
8224 int up = player_action & JOY_UP;
8225 int down = player_action & JOY_DOWN;
8226 int button1 = player_action & JOY_BUTTON_1;
8227 int button2 = player_action & JOY_BUTTON_2;
8228 int dx = (left ? -1 : right ? 1 : 0);
8229 int dy = (up ? -1 : down ? 1 : 0);
8231 if (!player->active || tape.pausing)
8237 snapped = SnapField(player, dx, dy);
8241 dropped = DropElement(player);
8243 moved = MovePlayer(player, dx, dy);
8246 if (tape.single_step && tape.recording && !tape.pausing)
8248 if (button1 || (dropped && !moved))
8250 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8251 SnapField(player, 0, 0); /* stop snapping */
8255 SetPlayerWaiting(player, FALSE);
8257 return player_action;
8261 /* no actions for this player (no input at player's configured device) */
8263 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8264 SnapField(player, 0, 0);
8265 CheckGravityMovementWhenNotMoving(player);
8267 if (player->MovPos == 0)
8268 SetPlayerWaiting(player, TRUE);
8270 if (player->MovPos == 0) /* needed for tape.playing */
8271 player->is_moving = FALSE;
8273 player->is_dropping = FALSE;
8279 void AdvanceFrameAndPlayerCounters(int player_nr)
8283 /* advance frame counters (global frame counter and time frame counter) */
8287 /* advance player counters (counters for move delay, move animation etc.) */
8288 for (i = 0; i < MAX_PLAYERS; i++)
8290 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8291 int move_delay_value = stored_player[i].move_delay_value;
8292 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8294 if (!advance_player_counters) /* not all players may be affected */
8297 #if USE_NEW_PLAYER_ANIM
8298 if (move_frames == 0) /* less than one move per game frame */
8300 int stepsize = TILEX / move_delay_value;
8301 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8302 int count = (stored_player[i].is_moving ?
8303 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8305 if (count % delay == 0)
8310 stored_player[i].Frame += move_frames;
8312 if (stored_player[i].MovPos != 0)
8313 stored_player[i].StepFrame += move_frames;
8315 if (stored_player[i].move_delay > 0)
8316 stored_player[i].move_delay--;
8318 /* due to bugs in previous versions, counter must count up, not down */
8319 if (stored_player[i].push_delay != -1)
8320 stored_player[i].push_delay++;
8322 if (stored_player[i].drop_delay > 0)
8323 stored_player[i].drop_delay--;
8329 static unsigned long game_frame_delay = 0;
8330 unsigned long game_frame_delay_value;
8331 int magic_wall_x = 0, magic_wall_y = 0;
8332 int i, x, y, element, graphic;
8333 byte *recorded_player_action;
8334 byte summarized_player_action = 0;
8335 byte tape_action[MAX_PLAYERS];
8337 if (game_status != GAME_MODE_PLAYING)
8340 game_frame_delay_value =
8341 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8343 if (tape.playing && tape.warp_forward && !tape.pausing)
8344 game_frame_delay_value = 0;
8346 /* ---------- main game synchronization point ---------- */
8348 InitPlayfieldScanModeVars();
8350 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8352 if (network_playing && !network_player_action_received)
8354 /* try to get network player actions in time */
8356 #if defined(NETWORK_AVALIABLE)
8357 /* last chance to get network player actions without main loop delay */
8361 /* game was quit by network peer */
8362 if (game_status != GAME_MODE_PLAYING)
8365 if (!network_player_action_received)
8366 return; /* failed to get network player actions in time */
8372 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8375 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8376 if (recorded_player_action == NULL && tape.pausing)
8380 for (i = 0; i < MAX_PLAYERS; i++)
8382 summarized_player_action |= stored_player[i].action;
8384 if (!network_playing)
8385 stored_player[i].effective_action = stored_player[i].action;
8388 #if defined(NETWORK_AVALIABLE)
8389 if (network_playing)
8390 SendToServer_MovePlayer(summarized_player_action);
8393 if (!options.network && !setup.team_mode)
8394 local_player->effective_action = summarized_player_action;
8396 if (recorded_player_action != NULL)
8397 for (i = 0; i < MAX_PLAYERS; i++)
8398 stored_player[i].effective_action = recorded_player_action[i];
8400 for (i = 0; i < MAX_PLAYERS; i++)
8402 tape_action[i] = stored_player[i].effective_action;
8404 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8405 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8408 /* only save actions from input devices, but not programmed actions */
8410 TapeRecordAction(tape_action);
8412 for (i = 0; i < MAX_PLAYERS; i++)
8414 int actual_player_action = stored_player[i].effective_action;
8417 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8418 - rnd_equinox_tetrachloride 048
8419 - rnd_equinox_tetrachloride_ii 096
8420 - rnd_emanuel_schmieg 002
8421 - doctor_sloan_ww 001, 020
8423 if (stored_player[i].MovPos == 0)
8424 CheckGravityMovement(&stored_player[i]);
8427 /* overwrite programmed action with tape action */
8428 if (stored_player[i].programmed_action)
8429 actual_player_action = stored_player[i].programmed_action;
8432 PlayerActions(&stored_player[i], actual_player_action);
8434 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8436 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8437 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8440 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8443 network_player_action_received = FALSE;
8445 ScrollScreen(NULL, SCROLL_GO_ON);
8447 /* for backwards compatibility, the following code emulates a fixed bug that
8448 occured when pushing elements (causing elements that just made their last
8449 pushing step to already (if possible) make their first falling step in the
8450 same game frame, which is bad); this code is also needed to use the famous
8451 "spring push bug" which is used in older levels and might be wanted to be
8452 used also in newer levels, but in this case the buggy pushing code is only
8453 affecting the "spring" element and no other elements */
8455 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8457 for (i = 0; i < MAX_PLAYERS; i++)
8459 struct PlayerInfo *player = &stored_player[i];
8463 if (player->active && player->is_pushing && player->is_moving &&
8465 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8466 Feld[x][y] == EL_SPRING))
8468 ContinueMoving(x, y);
8470 /* continue moving after pushing (this is actually a bug) */
8471 if (!IS_MOVING(x, y))
8480 SCAN_PLAYFIELD(x, y)
8482 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8485 ChangeCount[x][y] = 0;
8486 ChangeEvent[x][y] = -1;
8488 /* this must be handled before main playfield loop */
8489 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8492 if (MovDelay[x][y] <= 0)
8496 #if USE_NEW_SNAP_DELAY
8497 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8500 if (MovDelay[x][y] <= 0)
8503 DrawLevelField(x, y);
8505 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8511 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8513 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8514 printf("GameActions(): This should never happen!\n");
8516 ChangePage[x][y] = -1;
8521 if (WasJustMoving[x][y] > 0)
8522 WasJustMoving[x][y]--;
8523 if (WasJustFalling[x][y] > 0)
8524 WasJustFalling[x][y]--;
8525 if (CheckCollision[x][y] > 0)
8526 CheckCollision[x][y]--;
8530 /* reset finished pushing action (not done in ContinueMoving() to allow
8531 continous pushing animation for elements with zero push delay) */
8532 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8534 ResetGfxAnimation(x, y);
8535 DrawLevelField(x, y);
8539 if (IS_BLOCKED(x, y))
8543 Blocked2Moving(x, y, &oldx, &oldy);
8544 if (!IS_MOVING(oldx, oldy))
8546 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8547 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8548 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8549 printf("GameActions(): This should never happen!\n");
8556 SCAN_PLAYFIELD(x, y)
8558 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8561 element = Feld[x][y];
8562 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8565 printf("::: %d,%d\n", x, y);
8567 if (element == EL_ROCK)
8568 printf("::: Yo man! Rocks can fall!\n");
8571 if (graphic_info[graphic].anim_global_sync)
8572 GfxFrame[x][y] = FrameCounter;
8573 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8575 int old_gfx_frame = GfxFrame[x][y];
8577 GfxFrame[x][y] = CustomValue[x][y];
8580 if (GfxFrame[x][y] != old_gfx_frame)
8582 DrawLevelGraphicAnimation(x, y, graphic);
8584 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8586 int old_gfx_frame = GfxFrame[x][y];
8588 GfxFrame[x][y] = element_info[element].collect_score;
8591 if (GfxFrame[x][y] != old_gfx_frame)
8593 DrawLevelGraphicAnimation(x, y, graphic);
8596 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8597 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8598 ResetRandomAnimationValue(x, y);
8600 SetRandomAnimationValue(x, y);
8602 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8604 if (IS_INACTIVE(element))
8606 if (IS_ANIMATED(graphic))
8607 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8612 /* this may take place after moving, so 'element' may have changed */
8613 if (IS_CHANGING(x, y) &&
8614 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8616 int page = element_info[element].event_page_nr[CE_DELAY];
8618 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8622 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8626 if (element == EL_CUSTOM_255)
8627 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8631 HandleElementChange(x, y, page);
8633 if (CAN_CHANGE(element))
8634 HandleElementChange(x, y, page);
8636 if (HAS_ACTION(element))
8637 ExecuteCustomElementAction(x, y, element, page);
8642 element = Feld[x][y];
8643 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8646 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8650 element = Feld[x][y];
8651 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8653 if (IS_ANIMATED(graphic) &&
8656 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8658 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8659 EdelsteinFunkeln(x, y);
8661 else if ((element == EL_ACID ||
8662 element == EL_EXIT_OPEN ||
8663 element == EL_SP_EXIT_OPEN ||
8664 element == EL_SP_TERMINAL ||
8665 element == EL_SP_TERMINAL_ACTIVE ||
8666 element == EL_EXTRA_TIME ||
8667 element == EL_SHIELD_NORMAL ||
8668 element == EL_SHIELD_DEADLY) &&
8669 IS_ANIMATED(graphic))
8670 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8671 else if (IS_MOVING(x, y))
8672 ContinueMoving(x, y);
8673 else if (IS_ACTIVE_BOMB(element))
8674 CheckDynamite(x, y);
8675 else if (element == EL_AMOEBA_GROWING)
8676 AmoebeWaechst(x, y);
8677 else if (element == EL_AMOEBA_SHRINKING)
8678 AmoebaDisappearing(x, y);
8680 #if !USE_NEW_AMOEBA_CODE
8681 else if (IS_AMOEBALIVE(element))
8682 AmoebeAbleger(x, y);
8685 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8687 else if (element == EL_EXIT_CLOSED)
8689 else if (element == EL_SP_EXIT_CLOSED)
8691 else if (element == EL_EXPANDABLE_WALL_GROWING)
8693 else if (element == EL_EXPANDABLE_WALL ||
8694 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8695 element == EL_EXPANDABLE_WALL_VERTICAL ||
8696 element == EL_EXPANDABLE_WALL_ANY)
8698 else if (element == EL_FLAMES)
8699 CheckForDragon(x, y);
8700 else if (element == EL_EXPLOSION)
8701 ; /* drawing of correct explosion animation is handled separately */
8702 else if (element == EL_ELEMENT_SNAPPING)
8705 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8707 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8710 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8711 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8714 if (element == EL_CUSTOM_255 ||
8715 element == EL_CUSTOM_256)
8716 DrawLevelGraphicAnimation(x, y, graphic);
8719 if (IS_BELT_ACTIVE(element))
8720 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8722 if (game.magic_wall_active)
8724 int jx = local_player->jx, jy = local_player->jy;
8726 /* play the element sound at the position nearest to the player */
8727 if ((element == EL_MAGIC_WALL_FULL ||
8728 element == EL_MAGIC_WALL_ACTIVE ||
8729 element == EL_MAGIC_WALL_EMPTYING ||
8730 element == EL_BD_MAGIC_WALL_FULL ||
8731 element == EL_BD_MAGIC_WALL_ACTIVE ||
8732 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8733 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8741 #if USE_NEW_AMOEBA_CODE
8742 /* new experimental amoeba growth stuff */
8743 if (!(FrameCounter % 8))
8745 static unsigned long random = 1684108901;
8747 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8749 x = RND(lev_fieldx);
8750 y = RND(lev_fieldy);
8751 element = Feld[x][y];
8753 if (!IS_PLAYER(x,y) &&
8754 (element == EL_EMPTY ||
8755 CAN_GROW_INTO(element) ||
8756 element == EL_QUICKSAND_EMPTY ||
8757 element == EL_ACID_SPLASH_LEFT ||
8758 element == EL_ACID_SPLASH_RIGHT))
8760 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8761 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8762 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8763 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8764 Feld[x][y] = EL_AMOEBA_DROP;
8767 random = random * 129 + 1;
8773 if (game.explosions_delayed)
8776 game.explosions_delayed = FALSE;
8779 SCAN_PLAYFIELD(x, y)
8781 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8784 element = Feld[x][y];
8786 if (ExplodeField[x][y])
8787 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8788 else if (element == EL_EXPLOSION)
8789 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8791 ExplodeField[x][y] = EX_TYPE_NONE;
8794 game.explosions_delayed = TRUE;
8797 if (game.magic_wall_active)
8799 if (!(game.magic_wall_time_left % 4))
8801 int element = Feld[magic_wall_x][magic_wall_y];
8803 if (element == EL_BD_MAGIC_WALL_FULL ||
8804 element == EL_BD_MAGIC_WALL_ACTIVE ||
8805 element == EL_BD_MAGIC_WALL_EMPTYING)
8806 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8808 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8811 if (game.magic_wall_time_left > 0)
8813 game.magic_wall_time_left--;
8814 if (!game.magic_wall_time_left)
8817 SCAN_PLAYFIELD(x, y)
8819 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8822 element = Feld[x][y];
8824 if (element == EL_MAGIC_WALL_ACTIVE ||
8825 element == EL_MAGIC_WALL_FULL)
8827 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8828 DrawLevelField(x, y);
8830 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8831 element == EL_BD_MAGIC_WALL_FULL)
8833 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8834 DrawLevelField(x, y);
8838 game.magic_wall_active = FALSE;
8843 if (game.light_time_left > 0)
8845 game.light_time_left--;
8847 if (game.light_time_left == 0)
8848 RedrawAllLightSwitchesAndInvisibleElements();
8851 if (game.timegate_time_left > 0)
8853 game.timegate_time_left--;
8855 if (game.timegate_time_left == 0)
8856 CloseAllOpenTimegates();
8859 if (game.lenses_time_left > 0)
8861 game.lenses_time_left--;
8863 if (game.lenses_time_left == 0)
8864 RedrawAllInvisibleElementsForLenses();
8867 if (game.magnify_time_left > 0)
8869 game.magnify_time_left--;
8871 if (game.magnify_time_left == 0)
8872 RedrawAllInvisibleElementsForMagnifier();
8875 for (i = 0; i < MAX_PLAYERS; i++)
8877 struct PlayerInfo *player = &stored_player[i];
8879 if (SHIELD_ON(player))
8881 if (player->shield_deadly_time_left)
8882 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8883 else if (player->shield_normal_time_left)
8884 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8888 if (TimeFrames >= FRAMES_PER_SECOND)
8893 for (i = 0; i < MAX_PLAYERS; i++)
8895 struct PlayerInfo *player = &stored_player[i];
8897 if (SHIELD_ON(player))
8899 player->shield_normal_time_left--;
8901 if (player->shield_deadly_time_left > 0)
8902 player->shield_deadly_time_left--;
8906 if (!level.use_step_counter)
8914 if (TimeLeft <= 10 && setup.time_limit)
8915 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8917 DrawGameValue_Time(TimeLeft);
8919 if (!TimeLeft && setup.time_limit)
8920 for (i = 0; i < MAX_PLAYERS; i++)
8921 KillPlayer(&stored_player[i]);
8923 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8924 DrawGameValue_Time(TimePlayed);
8927 if (tape.recording || tape.playing)
8928 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8932 PlayAllPlayersSound();
8934 if (options.debug) /* calculate frames per second */
8936 static unsigned long fps_counter = 0;
8937 static int fps_frames = 0;
8938 unsigned long fps_delay_ms = Counter() - fps_counter;
8942 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
8944 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8947 fps_counter = Counter();
8950 redraw_mask |= REDRAW_FPS;
8953 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
8955 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8957 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8959 local_player->show_envelope = 0;
8962 /* use random number generator in every frame to make it less predictable */
8963 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8967 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8969 int min_x = x, min_y = y, max_x = x, max_y = y;
8972 for (i = 0; i < MAX_PLAYERS; i++)
8974 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8976 if (!stored_player[i].active || &stored_player[i] == player)
8979 min_x = MIN(min_x, jx);
8980 min_y = MIN(min_y, jy);
8981 max_x = MAX(max_x, jx);
8982 max_y = MAX(max_y, jy);
8985 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8988 static boolean AllPlayersInVisibleScreen()
8992 for (i = 0; i < MAX_PLAYERS; i++)
8994 int jx = stored_player[i].jx, jy = stored_player[i].jy;
8996 if (!stored_player[i].active)
8999 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9006 void ScrollLevel(int dx, int dy)
9008 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9011 BlitBitmap(drawto_field, drawto_field,
9012 FX + TILEX * (dx == -1) - softscroll_offset,
9013 FY + TILEY * (dy == -1) - softscroll_offset,
9014 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9015 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9016 FX + TILEX * (dx == 1) - softscroll_offset,
9017 FY + TILEY * (dy == 1) - softscroll_offset);
9021 x = (dx == 1 ? BX1 : BX2);
9022 for (y = BY1; y <= BY2; y++)
9023 DrawScreenField(x, y);
9028 y = (dy == 1 ? BY1 : BY2);
9029 for (x = BX1; x <= BX2; x++)
9030 DrawScreenField(x, y);
9033 redraw_mask |= REDRAW_FIELD;
9036 static boolean canFallDown(struct PlayerInfo *player)
9038 int jx = player->jx, jy = player->jy;
9040 return (IN_LEV_FIELD(jx, jy + 1) &&
9041 (IS_FREE(jx, jy + 1) ||
9042 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9043 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9044 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9047 static boolean canPassField(int x, int y, int move_dir)
9049 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9050 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9051 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9054 int element = Feld[x][y];
9056 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9057 !CAN_MOVE(element) &&
9058 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9059 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9060 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9063 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9065 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9066 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9067 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9071 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9072 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9073 (IS_DIGGABLE(Feld[newx][newy]) ||
9074 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9075 canPassField(newx, newy, move_dir)));
9078 static void CheckGravityMovement(struct PlayerInfo *player)
9080 if (game.gravity && !player->programmed_action)
9082 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9083 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9084 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9085 int jx = player->jx, jy = player->jy;
9086 boolean player_is_moving_to_valid_field =
9087 (!player_is_snapping &&
9088 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9089 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9090 boolean player_can_fall_down = canFallDown(player);
9092 if (player_can_fall_down &&
9093 !player_is_moving_to_valid_field)
9094 player->programmed_action = MV_DOWN;
9098 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9100 return CheckGravityMovement(player);
9102 if (game.gravity && !player->programmed_action)
9104 int jx = player->jx, jy = player->jy;
9105 boolean field_under_player_is_free =
9106 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9107 boolean player_is_standing_on_valid_field =
9108 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9109 (IS_WALKABLE(Feld[jx][jy]) &&
9110 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9112 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9113 player->programmed_action = MV_DOWN;
9119 -----------------------------------------------------------------------------
9120 dx, dy: direction (non-diagonal) to try to move the player to
9121 real_dx, real_dy: direction as read from input device (can be diagonal)
9124 boolean MovePlayerOneStep(struct PlayerInfo *player,
9125 int dx, int dy, int real_dx, int real_dy)
9127 int jx = player->jx, jy = player->jy;
9128 int new_jx = jx + dx, new_jy = jy + dy;
9131 boolean player_can_move = !player->cannot_move;
9133 if (!player->active || (!dx && !dy))
9134 return MF_NO_ACTION;
9136 player->MovDir = (dx < 0 ? MV_LEFT :
9139 dy > 0 ? MV_DOWN : MV_NONE);
9141 if (!IN_LEV_FIELD(new_jx, new_jy))
9142 return MF_NO_ACTION;
9144 if (!player_can_move)
9147 if (player->MovPos == 0)
9149 player->is_moving = FALSE;
9150 player->is_digging = FALSE;
9151 player->is_collecting = FALSE;
9152 player->is_snapping = FALSE;
9153 player->is_pushing = FALSE;
9156 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9157 SnapField(player, 0, 0);
9161 return MF_NO_ACTION;
9165 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9166 return MF_NO_ACTION;
9168 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9170 if (player_can_move && DONT_RUN_INTO(element))
9172 if (element == EL_ACID && dx == 0 && dy == 1)
9174 SplashAcid(new_jx, new_jy);
9175 Feld[jx][jy] = EL_PLAYER_1;
9176 InitMovingField(jx, jy, MV_DOWN);
9177 Store[jx][jy] = EL_ACID;
9178 ContinueMoving(jx, jy);
9182 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9187 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9188 if (can_move != MF_MOVING)
9191 /* check if DigField() has caused relocation of the player */
9192 if (player->jx != jx || player->jy != jy)
9193 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9195 StorePlayer[jx][jy] = 0;
9196 player->last_jx = jx;
9197 player->last_jy = jy;
9198 player->jx = new_jx;
9199 player->jy = new_jy;
9200 StorePlayer[new_jx][new_jy] = player->element_nr;
9202 if (player->move_delay_value_next != -1)
9204 player->move_delay_value = player->move_delay_value_next;
9205 player->move_delay_value_next = -1;
9209 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9211 player->step_counter++;
9213 PlayerVisit[jx][jy] = FrameCounter;
9215 ScrollPlayer(player, SCROLL_INIT);
9220 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9222 int jx = player->jx, jy = player->jy;
9223 int old_jx = jx, old_jy = jy;
9224 int moved = MF_NO_ACTION;
9226 if (!player->active)
9231 if (player->MovPos == 0)
9233 player->is_moving = FALSE;
9234 player->is_digging = FALSE;
9235 player->is_collecting = FALSE;
9236 player->is_snapping = FALSE;
9237 player->is_pushing = FALSE;
9243 if (player->move_delay > 0)
9246 player->move_delay = -1; /* set to "uninitialized" value */
9248 /* store if player is automatically moved to next field */
9249 player->is_auto_moving = (player->programmed_action != MV_NONE);
9251 /* remove the last programmed player action */
9252 player->programmed_action = 0;
9256 /* should only happen if pre-1.2 tape recordings are played */
9257 /* this is only for backward compatibility */
9259 int original_move_delay_value = player->move_delay_value;
9262 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9266 /* scroll remaining steps with finest movement resolution */
9267 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9269 while (player->MovPos)
9271 ScrollPlayer(player, SCROLL_GO_ON);
9272 ScrollScreen(NULL, SCROLL_GO_ON);
9274 AdvanceFrameAndPlayerCounters(player->index_nr);
9280 player->move_delay_value = original_move_delay_value;
9283 if (player->last_move_dir & MV_HORIZONTAL)
9285 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9286 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9290 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9291 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9297 if (moved & MF_MOVING && !ScreenMovPos &&
9298 (player == local_player || !options.network))
9300 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9301 int offset = (setup.scroll_delay ? 3 : 0);
9303 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9305 /* actual player has left the screen -- scroll in that direction */
9306 if (jx != old_jx) /* player has moved horizontally */
9307 scroll_x += (jx - old_jx);
9308 else /* player has moved vertically */
9309 scroll_y += (jy - old_jy);
9313 if (jx != old_jx) /* player has moved horizontally */
9315 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9316 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9317 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9319 /* don't scroll over playfield boundaries */
9320 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9321 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9323 /* don't scroll more than one field at a time */
9324 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9326 /* don't scroll against the player's moving direction */
9327 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9328 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9329 scroll_x = old_scroll_x;
9331 else /* player has moved vertically */
9333 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9334 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9335 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9337 /* don't scroll over playfield boundaries */
9338 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9339 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9341 /* don't scroll more than one field at a time */
9342 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9344 /* don't scroll against the player's moving direction */
9345 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9346 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9347 scroll_y = old_scroll_y;
9351 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9353 if (!options.network && !AllPlayersInVisibleScreen())
9355 scroll_x = old_scroll_x;
9356 scroll_y = old_scroll_y;
9360 ScrollScreen(player, SCROLL_INIT);
9361 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9366 player->StepFrame = 0;
9368 if (moved & MF_MOVING)
9370 if (old_jx != jx && old_jy == jy)
9371 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9372 else if (old_jx == jx && old_jy != jy)
9373 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9375 DrawLevelField(jx, jy); /* for "crumbled sand" */
9377 player->last_move_dir = player->MovDir;
9378 player->is_moving = TRUE;
9379 player->is_snapping = FALSE;
9380 player->is_switching = FALSE;
9381 player->is_dropping = FALSE;
9385 CheckGravityMovementWhenNotMoving(player);
9387 player->is_moving = FALSE;
9389 /* at this point, the player is allowed to move, but cannot move right now
9390 (e.g. because of something blocking the way) -- ensure that the player
9391 is also allowed to move in the next frame (in old versions before 3.1.1,
9392 the player was forced to wait again for eight frames before next try) */
9394 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9395 player->move_delay = 0; /* allow direct movement in the next frame */
9398 if (player->move_delay == -1) /* not yet initialized by DigField() */
9399 player->move_delay = player->move_delay_value;
9401 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9403 TestIfPlayerTouchesBadThing(jx, jy);
9404 TestIfPlayerTouchesCustomElement(jx, jy);
9407 if (!player->active)
9408 RemovePlayer(player);
9413 void ScrollPlayer(struct PlayerInfo *player, int mode)
9415 int jx = player->jx, jy = player->jy;
9416 int last_jx = player->last_jx, last_jy = player->last_jy;
9417 int move_stepsize = TILEX / player->move_delay_value;
9419 #if USE_NEW_PLAYER_SPEED
9420 if (!player->active)
9423 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9426 if (!player->active || player->MovPos == 0)
9430 if (mode == SCROLL_INIT)
9432 player->actual_frame_counter = FrameCounter;
9433 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9435 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9436 Feld[last_jx][last_jy] == EL_EMPTY)
9438 int last_field_block_delay = 0; /* start with no blocking at all */
9439 int block_delay_adjustment = player->block_delay_adjustment;
9441 /* if player blocks last field, add delay for exactly one move */
9442 if (player->block_last_field)
9444 last_field_block_delay += player->move_delay_value;
9446 /* when blocking enabled, prevent moving up despite gravity */
9447 if (game.gravity && player->MovDir == MV_UP)
9448 block_delay_adjustment = -1;
9451 /* add block delay adjustment (also possible when not blocking) */
9452 last_field_block_delay += block_delay_adjustment;
9454 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9455 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9458 #if USE_NEW_PLAYER_SPEED
9459 if (player->MovPos != 0) /* player has not yet reached destination */
9465 else if (!FrameReached(&player->actual_frame_counter, 1))
9469 printf("::: player->MovPos: %d -> %d\n",
9471 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9474 #if USE_NEW_PLAYER_SPEED
9475 if (player->MovPos != 0)
9477 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9478 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9480 /* before DrawPlayer() to draw correct player graphic for this case */
9481 if (player->MovPos == 0)
9482 CheckGravityMovement(player);
9485 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9486 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9488 /* before DrawPlayer() to draw correct player graphic for this case */
9489 if (player->MovPos == 0)
9490 CheckGravityMovement(player);
9493 if (player->MovPos == 0) /* player reached destination field */
9496 printf("::: player reached destination field\n");
9499 if (player->move_delay_reset_counter > 0)
9501 player->move_delay_reset_counter--;
9503 if (player->move_delay_reset_counter == 0)
9505 /* continue with normal speed after quickly moving through gate */
9506 HALVE_PLAYER_SPEED(player);
9508 /* be able to make the next move without delay */
9509 player->move_delay = 0;
9513 player->last_jx = jx;
9514 player->last_jy = jy;
9516 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9517 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9518 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9520 DrawPlayer(player); /* needed here only to cleanup last field */
9521 RemovePlayer(player);
9523 if (local_player->friends_still_needed == 0 ||
9524 IS_SP_ELEMENT(Feld[jx][jy]))
9525 player->LevelSolved = player->GameOver = TRUE;
9528 /* this breaks one level: "machine", level 000 */
9530 int move_direction = player->MovDir;
9531 int enter_side = MV_DIR_OPPOSITE(move_direction);
9532 int leave_side = move_direction;
9533 int old_jx = last_jx;
9534 int old_jy = last_jy;
9535 int old_element = Feld[old_jx][old_jy];
9536 int new_element = Feld[jx][jy];
9538 if (IS_CUSTOM_ELEMENT(old_element))
9539 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9541 player->index_bit, leave_side);
9543 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9545 player->index_bit, leave_side);
9547 if (IS_CUSTOM_ELEMENT(new_element))
9548 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9549 player->index_bit, enter_side);
9551 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9553 player->index_bit, enter_side);
9555 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9556 CE_MOVE_OF_X, move_direction);
9559 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9561 TestIfPlayerTouchesBadThing(jx, jy);
9562 TestIfPlayerTouchesCustomElement(jx, jy);
9564 /* needed because pushed element has not yet reached its destination,
9565 so it would trigger a change event at its previous field location */
9566 if (!player->is_pushing)
9567 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9569 if (!player->active)
9570 RemovePlayer(player);
9573 if (level.use_step_counter)
9583 if (TimeLeft <= 10 && setup.time_limit)
9584 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9586 DrawGameValue_Time(TimeLeft);
9588 if (!TimeLeft && setup.time_limit)
9589 for (i = 0; i < MAX_PLAYERS; i++)
9590 KillPlayer(&stored_player[i]);
9592 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9593 DrawGameValue_Time(TimePlayed);
9596 if (tape.single_step && tape.recording && !tape.pausing &&
9597 !player->programmed_action)
9598 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9602 void ScrollScreen(struct PlayerInfo *player, int mode)
9604 static unsigned long screen_frame_counter = 0;
9606 if (mode == SCROLL_INIT)
9608 /* set scrolling step size according to actual player's moving speed */
9609 ScrollStepSize = TILEX / player->move_delay_value;
9611 screen_frame_counter = FrameCounter;
9612 ScreenMovDir = player->MovDir;
9613 ScreenMovPos = player->MovPos;
9614 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9617 else if (!FrameReached(&screen_frame_counter, 1))
9622 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9623 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9624 redraw_mask |= REDRAW_FIELD;
9627 ScreenMovDir = MV_NONE;
9630 void TestIfPlayerTouchesCustomElement(int x, int y)
9632 static int xy[4][2] =
9639 static int trigger_sides[4][2] =
9641 /* center side border side */
9642 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9643 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9644 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9645 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9647 static int touch_dir[4] =
9654 int center_element = Feld[x][y]; /* should always be non-moving! */
9657 for (i = 0; i < NUM_DIRECTIONS; i++)
9659 int xx = x + xy[i][0];
9660 int yy = y + xy[i][1];
9661 int center_side = trigger_sides[i][0];
9662 int border_side = trigger_sides[i][1];
9665 if (!IN_LEV_FIELD(xx, yy))
9668 if (IS_PLAYER(x, y))
9670 struct PlayerInfo *player = PLAYERINFO(x, y);
9672 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9673 border_element = Feld[xx][yy]; /* may be moving! */
9674 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9675 border_element = Feld[xx][yy];
9676 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9677 border_element = MovingOrBlocked2Element(xx, yy);
9679 continue; /* center and border element do not touch */
9681 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9682 player->index_bit, border_side);
9683 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9684 CE_PLAYER_TOUCHES_X,
9685 player->index_bit, border_side);
9687 else if (IS_PLAYER(xx, yy))
9689 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9691 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9693 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9694 continue; /* center and border element do not touch */
9697 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9698 player->index_bit, center_side);
9699 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9700 CE_PLAYER_TOUCHES_X,
9701 player->index_bit, center_side);
9707 void TestIfElementTouchesCustomElement(int x, int y)
9709 static int xy[4][2] =
9716 static int trigger_sides[4][2] =
9718 /* center side border side */
9719 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9720 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9721 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9722 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9724 static int touch_dir[4] =
9731 boolean change_center_element = FALSE;
9732 int center_element = Feld[x][y]; /* should always be non-moving! */
9735 for (i = 0; i < NUM_DIRECTIONS; i++)
9737 int xx = x + xy[i][0];
9738 int yy = y + xy[i][1];
9739 int center_side = trigger_sides[i][0];
9740 int border_side = trigger_sides[i][1];
9743 if (!IN_LEV_FIELD(xx, yy))
9746 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9747 border_element = Feld[xx][yy]; /* may be moving! */
9748 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9749 border_element = Feld[xx][yy];
9750 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9751 border_element = MovingOrBlocked2Element(xx, yy);
9753 continue; /* center and border element do not touch */
9755 /* check for change of center element (but change it only once) */
9756 if (!change_center_element)
9757 change_center_element =
9758 CheckElementChangeBySide(x, y, center_element, border_element,
9759 CE_TOUCHING_X, border_side);
9761 /* check for change of border element */
9762 CheckElementChangeBySide(xx, yy, border_element, center_element,
9763 CE_TOUCHING_X, center_side);
9767 void TestIfElementHitsCustomElement(int x, int y, int direction)
9769 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9770 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9771 int hitx = x + dx, hity = y + dy;
9772 int hitting_element = Feld[x][y];
9773 int touched_element;
9775 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9778 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9779 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9781 if (IN_LEV_FIELD(hitx, hity))
9783 int opposite_direction = MV_DIR_OPPOSITE(direction);
9784 int hitting_side = direction;
9785 int touched_side = opposite_direction;
9786 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9787 MovDir[hitx][hity] != direction ||
9788 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9794 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9795 CE_HITTING_X, touched_side);
9797 CheckElementChangeBySide(hitx, hity, touched_element,
9798 hitting_element, CE_HIT_BY_X, hitting_side);
9800 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9801 CE_HIT_BY_SOMETHING, opposite_direction);
9805 /* "hitting something" is also true when hitting the playfield border */
9806 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9807 CE_HITTING_SOMETHING, direction);
9811 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9813 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9814 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9815 int hitx = x + dx, hity = y + dy;
9816 int hitting_element = Feld[x][y];
9817 int touched_element;
9819 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9820 !IS_FREE(hitx, hity) &&
9821 (!IS_MOVING(hitx, hity) ||
9822 MovDir[hitx][hity] != direction ||
9823 ABS(MovPos[hitx][hity]) <= TILEY / 2));
9826 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9830 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9834 touched_element = (IN_LEV_FIELD(hitx, hity) ?
9835 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9837 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9838 EP_CAN_SMASH_EVERYTHING, direction);
9840 if (IN_LEV_FIELD(hitx, hity))
9842 int opposite_direction = MV_DIR_OPPOSITE(direction);
9843 int hitting_side = direction;
9844 int touched_side = opposite_direction;
9846 int touched_element = MovingOrBlocked2Element(hitx, hity);
9849 boolean object_hit = (!IS_MOVING(hitx, hity) ||
9850 MovDir[hitx][hity] != direction ||
9851 ABS(MovPos[hitx][hity]) <= TILEY / 2);
9860 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9861 CE_SMASHED_BY_SOMETHING, opposite_direction);
9863 CheckElementChangeBySide(x, y, hitting_element, touched_element,
9864 CE_OTHER_IS_SMASHING, touched_side);
9866 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9867 CE_OTHER_GETS_SMASHED, hitting_side);
9873 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9875 int i, kill_x = -1, kill_y = -1;
9876 int bad_element = -1;
9877 static int test_xy[4][2] =
9884 static int test_dir[4] =
9892 for (i = 0; i < NUM_DIRECTIONS; i++)
9894 int test_x, test_y, test_move_dir, test_element;
9896 test_x = good_x + test_xy[i][0];
9897 test_y = good_y + test_xy[i][1];
9899 if (!IN_LEV_FIELD(test_x, test_y))
9903 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9905 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9907 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9908 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9910 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9911 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
9915 bad_element = test_element;
9921 if (kill_x != -1 || kill_y != -1)
9923 if (IS_PLAYER(good_x, good_y))
9925 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9927 if (player->shield_deadly_time_left > 0 &&
9928 !IS_INDESTRUCTIBLE(bad_element))
9929 Bang(kill_x, kill_y);
9930 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9934 Bang(good_x, good_y);
9938 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9940 int i, kill_x = -1, kill_y = -1;
9941 int bad_element = Feld[bad_x][bad_y];
9942 static int test_xy[4][2] =
9949 static int touch_dir[4] =
9956 static int test_dir[4] =
9964 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
9967 for (i = 0; i < NUM_DIRECTIONS; i++)
9969 int test_x, test_y, test_move_dir, test_element;
9971 test_x = bad_x + test_xy[i][0];
9972 test_y = bad_y + test_xy[i][1];
9973 if (!IN_LEV_FIELD(test_x, test_y))
9977 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9979 test_element = Feld[test_x][test_y];
9981 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9982 2nd case: DONT_TOUCH style bad thing does not move away from good thing
9984 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
9985 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
9987 /* good thing is player or penguin that does not move away */
9988 if (IS_PLAYER(test_x, test_y))
9990 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9992 if (bad_element == EL_ROBOT && player->is_moving)
9993 continue; /* robot does not kill player if he is moving */
9995 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9997 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9998 continue; /* center and border element do not touch */
10005 else if (test_element == EL_PENGUIN)
10014 if (kill_x != -1 || kill_y != -1)
10016 if (IS_PLAYER(kill_x, kill_y))
10018 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10020 if (player->shield_deadly_time_left > 0 &&
10021 !IS_INDESTRUCTIBLE(bad_element))
10022 Bang(bad_x, bad_y);
10023 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10024 KillPlayer(player);
10027 Bang(kill_x, kill_y);
10031 void TestIfPlayerTouchesBadThing(int x, int y)
10033 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10036 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10038 TestIfGoodThingHitsBadThing(x, y, move_dir);
10041 void TestIfBadThingTouchesPlayer(int x, int y)
10043 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10046 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10048 TestIfBadThingHitsGoodThing(x, y, move_dir);
10051 void TestIfFriendTouchesBadThing(int x, int y)
10053 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10056 void TestIfBadThingTouchesFriend(int x, int y)
10058 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10061 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10063 int i, kill_x = bad_x, kill_y = bad_y;
10064 static int xy[4][2] =
10072 for (i = 0; i < NUM_DIRECTIONS; i++)
10076 x = bad_x + xy[i][0];
10077 y = bad_y + xy[i][1];
10078 if (!IN_LEV_FIELD(x, y))
10081 element = Feld[x][y];
10082 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10083 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10091 if (kill_x != bad_x || kill_y != bad_y)
10092 Bang(bad_x, bad_y);
10095 void KillPlayer(struct PlayerInfo *player)
10097 int jx = player->jx, jy = player->jy;
10099 if (!player->active)
10102 /* remove accessible field at the player's position */
10103 Feld[jx][jy] = EL_EMPTY;
10105 /* deactivate shield (else Bang()/Explode() would not work right) */
10106 player->shield_normal_time_left = 0;
10107 player->shield_deadly_time_left = 0;
10110 BuryPlayer(player);
10113 static void KillPlayerUnlessEnemyProtected(int x, int y)
10115 if (!PLAYER_ENEMY_PROTECTED(x, y))
10116 KillPlayer(PLAYERINFO(x, y));
10119 static void KillPlayerUnlessExplosionProtected(int x, int y)
10121 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10122 KillPlayer(PLAYERINFO(x, y));
10125 void BuryPlayer(struct PlayerInfo *player)
10127 int jx = player->jx, jy = player->jy;
10129 if (!player->active)
10132 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10133 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10135 player->GameOver = TRUE;
10136 RemovePlayer(player);
10139 void RemovePlayer(struct PlayerInfo *player)
10141 int jx = player->jx, jy = player->jy;
10142 int i, found = FALSE;
10144 player->present = FALSE;
10145 player->active = FALSE;
10147 if (!ExplodeField[jx][jy])
10148 StorePlayer[jx][jy] = 0;
10150 if (player->is_moving)
10151 DrawLevelField(player->last_jx, player->last_jy);
10153 for (i = 0; i < MAX_PLAYERS; i++)
10154 if (stored_player[i].active)
10158 AllPlayersGone = TRUE;
10164 #if USE_NEW_SNAP_DELAY
10165 static void setFieldForSnapping(int x, int y, int element, int direction)
10167 struct ElementInfo *ei = &element_info[element];
10168 int direction_bit = MV_DIR_BIT(direction);
10169 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10170 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10171 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10173 Feld[x][y] = EL_ELEMENT_SNAPPING;
10174 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10176 ResetGfxAnimation(x, y);
10178 GfxElement[x][y] = element;
10179 GfxAction[x][y] = action;
10180 GfxDir[x][y] = direction;
10181 GfxFrame[x][y] = -1;
10186 =============================================================================
10187 checkDiagonalPushing()
10188 -----------------------------------------------------------------------------
10189 check if diagonal input device direction results in pushing of object
10190 (by checking if the alternative direction is walkable, diggable, ...)
10191 =============================================================================
10194 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10195 int x, int y, int real_dx, int real_dy)
10197 int jx, jy, dx, dy, xx, yy;
10199 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10202 /* diagonal direction: check alternative direction */
10207 xx = jx + (dx == 0 ? real_dx : 0);
10208 yy = jy + (dy == 0 ? real_dy : 0);
10210 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10214 =============================================================================
10216 -----------------------------------------------------------------------------
10217 x, y: field next to player (non-diagonal) to try to dig to
10218 real_dx, real_dy: direction as read from input device (can be diagonal)
10219 =============================================================================
10222 int DigField(struct PlayerInfo *player,
10223 int oldx, int oldy, int x, int y,
10224 int real_dx, int real_dy, int mode)
10226 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10227 boolean player_was_pushing = player->is_pushing;
10228 boolean player_can_enter = (!player->cannot_move || mode == DF_SNAP);
10229 int jx = oldx, jy = oldy;
10230 int dx = x - jx, dy = y - jy;
10231 int nextx = x + dx, nexty = y + dy;
10232 int move_direction = (dx == -1 ? MV_LEFT :
10233 dx == +1 ? MV_RIGHT :
10235 dy == +1 ? MV_DOWN : MV_NONE);
10236 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10237 int dig_side = MV_DIR_OPPOSITE(move_direction);
10238 int old_element = Feld[jx][jy];
10242 if (is_player) /* function can also be called by EL_PENGUIN */
10244 if (player->MovPos == 0)
10246 player->is_digging = FALSE;
10247 player->is_collecting = FALSE;
10250 if (player->MovPos == 0) /* last pushing move finished */
10251 player->is_pushing = FALSE;
10253 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10255 player->is_switching = FALSE;
10256 player->push_delay = -1;
10258 return MF_NO_ACTION;
10262 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10263 return MF_NO_ACTION;
10265 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10266 old_element = Back[jx][jy];
10268 /* in case of element dropped at player position, check background */
10269 else if (Back[jx][jy] != EL_EMPTY &&
10270 game.engine_version >= VERSION_IDENT(2,2,0,0))
10271 old_element = Back[jx][jy];
10273 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10274 return MF_NO_ACTION; /* field has no opening in this direction */
10276 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10277 return MF_NO_ACTION; /* field has no opening in this direction */
10279 element = Feld[x][y];
10280 #if USE_NEW_CUSTOM_VALUE
10283 collect_count = element_info[element].collect_count_initial;
10285 collect_count = CustomValue[x][y];
10289 collect_count = element_info[element].collect_count_initial;
10293 if (element != EL_BLOCKED &&
10294 CustomValue[x][y] != element_info[element].collect_count_initial)
10295 printf("::: %d: %d != %d\n",
10298 element_info[element].collect_count_initial);
10301 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10302 return MF_NO_ACTION;
10304 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10305 game.engine_version >= VERSION_IDENT(2,2,0,0))
10307 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10308 player->index_bit, dig_side);
10309 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10310 player->index_bit, dig_side);
10312 if (Feld[x][y] != element) /* field changed by snapping */
10315 return MF_NO_ACTION;
10318 if (game.gravity && is_player && !player->is_auto_moving &&
10319 canFallDown(player) && move_direction != MV_DOWN &&
10320 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10321 return MF_NO_ACTION; /* player cannot walk here due to gravity */
10323 if (player_can_enter &&
10324 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10326 int sound_element = SND_ELEMENT(element);
10327 int sound_action = ACTION_WALKING;
10329 if (IS_RND_GATE(element))
10331 if (!player->key[RND_GATE_NR(element)])
10332 return MF_NO_ACTION;
10334 else if (IS_RND_GATE_GRAY(element))
10336 if (!player->key[RND_GATE_GRAY_NR(element)])
10337 return MF_NO_ACTION;
10339 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10341 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10342 return MF_NO_ACTION;
10344 else if (element == EL_EXIT_OPEN ||
10345 element == EL_SP_EXIT_OPEN ||
10346 element == EL_SP_EXIT_OPENING)
10348 sound_action = ACTION_PASSING; /* player is passing exit */
10350 else if (element == EL_EMPTY)
10352 sound_action = ACTION_MOVING; /* nothing to walk on */
10355 /* play sound from background or player, whatever is available */
10356 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10357 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10359 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10361 else if (player_can_enter &&
10362 IS_PASSABLE(element) && canPassField(x, y, move_direction))
10364 if (!ACCESS_FROM(element, opposite_direction))
10365 return MF_NO_ACTION; /* field not accessible from this direction */
10367 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10368 return MF_NO_ACTION;
10370 if (IS_EM_GATE(element))
10372 if (!player->key[EM_GATE_NR(element)])
10373 return MF_NO_ACTION;
10375 else if (IS_EM_GATE_GRAY(element))
10377 if (!player->key[EM_GATE_GRAY_NR(element)])
10378 return MF_NO_ACTION;
10380 else if (IS_EM_GATE_GRAY_ACTIVE(element))
10382 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10383 return MF_NO_ACTION;
10385 else if (IS_SP_PORT(element))
10387 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10388 element == EL_SP_GRAVITY_PORT_RIGHT ||
10389 element == EL_SP_GRAVITY_PORT_UP ||
10390 element == EL_SP_GRAVITY_PORT_DOWN)
10391 game.gravity = !game.gravity;
10392 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10393 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10394 element == EL_SP_GRAVITY_ON_PORT_UP ||
10395 element == EL_SP_GRAVITY_ON_PORT_DOWN)
10396 game.gravity = TRUE;
10397 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10398 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10399 element == EL_SP_GRAVITY_OFF_PORT_UP ||
10400 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10401 game.gravity = FALSE;
10404 /* automatically move to the next field with double speed */
10405 player->programmed_action = move_direction;
10407 if (player->move_delay_reset_counter == 0)
10409 player->move_delay_reset_counter = 2; /* two double speed steps */
10411 DOUBLE_PLAYER_SPEED(player);
10414 PlayLevelSoundAction(x, y, ACTION_PASSING);
10416 else if (player_can_enter && IS_DIGGABLE(element))
10420 if (mode != DF_SNAP)
10422 GfxElement[x][y] = GFX_ELEMENT(element);
10423 player->is_digging = TRUE;
10426 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10428 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10429 player->index_bit, dig_side);
10431 if (mode == DF_SNAP)
10433 #if USE_NEW_SNAP_DELAY
10434 if (level.block_snap_field)
10435 setFieldForSnapping(x, y, element, move_direction);
10437 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10439 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10442 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10443 player->index_bit, dig_side);
10446 else if (player_can_enter && IS_COLLECTIBLE(element))
10450 if (is_player && mode != DF_SNAP)
10452 GfxElement[x][y] = element;
10453 player->is_collecting = TRUE;
10456 if (element == EL_SPEED_PILL)
10458 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10460 else if (element == EL_EXTRA_TIME && level.time > 0)
10462 TimeLeft += level.extra_time;
10463 DrawGameValue_Time(TimeLeft);
10465 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10467 player->shield_normal_time_left += level.shield_normal_time;
10468 if (element == EL_SHIELD_DEADLY)
10469 player->shield_deadly_time_left += level.shield_deadly_time;
10471 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10473 if (player->inventory_size < MAX_INVENTORY_SIZE)
10474 player->inventory_element[player->inventory_size++] = element;
10476 DrawGameValue_Dynamite(local_player->inventory_size);
10478 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10480 player->dynabomb_count++;
10481 player->dynabombs_left++;
10483 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10485 player->dynabomb_size++;
10487 else if (element == EL_DYNABOMB_INCREASE_POWER)
10489 player->dynabomb_xl = TRUE;
10491 else if (IS_KEY(element))
10493 player->key[KEY_NR(element)] = TRUE;
10495 DrawGameValue_Keys(player->key);
10497 redraw_mask |= REDRAW_DOOR_1;
10499 else if (IS_ENVELOPE(element))
10501 player->show_envelope = element;
10503 else if (element == EL_EMC_LENSES)
10505 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10507 RedrawAllInvisibleElementsForLenses();
10509 else if (element == EL_EMC_MAGNIFIER)
10511 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10513 RedrawAllInvisibleElementsForMagnifier();
10515 else if (IS_DROPPABLE(element) ||
10516 IS_THROWABLE(element)) /* can be collected and dropped */
10520 if (collect_count == 0)
10521 player->inventory_infinite_element = element;
10523 for (i = 0; i < collect_count; i++)
10524 if (player->inventory_size < MAX_INVENTORY_SIZE)
10525 player->inventory_element[player->inventory_size++] = element;
10527 DrawGameValue_Dynamite(local_player->inventory_size);
10529 else if (collect_count > 0)
10531 local_player->gems_still_needed -= collect_count;
10532 if (local_player->gems_still_needed < 0)
10533 local_player->gems_still_needed = 0;
10535 DrawGameValue_Emeralds(local_player->gems_still_needed);
10538 RaiseScoreElement(element);
10539 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10542 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10543 player->index_bit, dig_side);
10545 if (mode == DF_SNAP)
10547 #if USE_NEW_SNAP_DELAY
10548 if (level.block_snap_field)
10549 setFieldForSnapping(x, y, element, move_direction);
10551 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10553 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10556 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10557 player->index_bit, dig_side);
10560 else if (player_can_enter && IS_PUSHABLE(element))
10562 if (mode == DF_SNAP && element != EL_BD_ROCK)
10563 return MF_NO_ACTION;
10565 if (CAN_FALL(element) && dy)
10566 return MF_NO_ACTION;
10568 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10569 !(element == EL_SPRING && level.use_spring_bug))
10570 return MF_NO_ACTION;
10572 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10573 ((move_direction & MV_VERTICAL &&
10574 ((element_info[element].move_pattern & MV_LEFT &&
10575 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10576 (element_info[element].move_pattern & MV_RIGHT &&
10577 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10578 (move_direction & MV_HORIZONTAL &&
10579 ((element_info[element].move_pattern & MV_UP &&
10580 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10581 (element_info[element].move_pattern & MV_DOWN &&
10582 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10583 return MF_NO_ACTION;
10585 /* do not push elements already moving away faster than player */
10586 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10587 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10588 return MF_NO_ACTION;
10590 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10592 if (player->push_delay_value == -1 || !player_was_pushing)
10593 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10595 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10597 if (player->push_delay_value == -1)
10598 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10600 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10602 if (!player->is_pushing)
10603 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10606 player->is_pushing = TRUE;
10608 if (!(IN_LEV_FIELD(nextx, nexty) &&
10609 (IS_FREE(nextx, nexty) ||
10610 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10611 IS_SB_ELEMENT(element)))))
10612 return MF_NO_ACTION;
10614 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10615 return MF_NO_ACTION;
10617 if (player->push_delay == -1) /* new pushing; restart delay */
10618 player->push_delay = 0;
10620 if (player->push_delay < player->push_delay_value &&
10621 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10622 element != EL_SPRING && element != EL_BALLOON)
10624 /* make sure that there is no move delay before next try to push */
10625 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10626 player->move_delay = 0;
10628 return MF_NO_ACTION;
10631 if (IS_SB_ELEMENT(element))
10633 if (element == EL_SOKOBAN_FIELD_FULL)
10635 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10636 local_player->sokobanfields_still_needed++;
10639 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10641 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10642 local_player->sokobanfields_still_needed--;
10645 Feld[x][y] = EL_SOKOBAN_OBJECT;
10647 if (Back[x][y] == Back[nextx][nexty])
10648 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10649 else if (Back[x][y] != 0)
10650 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10653 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10656 if (local_player->sokobanfields_still_needed == 0 &&
10657 game.emulation == EMU_SOKOBAN)
10659 player->LevelSolved = player->GameOver = TRUE;
10660 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10664 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10666 InitMovingField(x, y, move_direction);
10667 GfxAction[x][y] = ACTION_PUSHING;
10669 if (mode == DF_SNAP)
10670 ContinueMoving(x, y);
10672 MovPos[x][y] = (dx != 0 ? dx : dy);
10674 Pushed[x][y] = TRUE;
10675 Pushed[nextx][nexty] = TRUE;
10677 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10678 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10680 player->push_delay_value = -1; /* get new value later */
10682 /* check for element change _after_ element has been pushed */
10683 if (game.use_change_when_pushing_bug)
10685 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10686 player->index_bit, dig_side);
10687 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10688 player->index_bit, dig_side);
10691 else if (IS_SWITCHABLE(element))
10693 if (PLAYER_SWITCHING(player, x, y))
10695 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10696 player->index_bit, dig_side);
10701 player->is_switching = TRUE;
10702 player->switch_x = x;
10703 player->switch_y = y;
10705 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10707 if (element == EL_ROBOT_WHEEL)
10709 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10713 DrawLevelField(x, y);
10715 else if (element == EL_SP_TERMINAL)
10720 SCAN_PLAYFIELD(xx, yy)
10722 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10725 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10727 else if (Feld[xx][yy] == EL_SP_TERMINAL)
10728 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10731 else if (IS_BELT_SWITCH(element))
10733 ToggleBeltSwitch(x, y);
10735 else if (element == EL_SWITCHGATE_SWITCH_UP ||
10736 element == EL_SWITCHGATE_SWITCH_DOWN)
10738 ToggleSwitchgateSwitch(x, y);
10740 else if (element == EL_LIGHT_SWITCH ||
10741 element == EL_LIGHT_SWITCH_ACTIVE)
10743 ToggleLightSwitch(x, y);
10745 else if (element == EL_TIMEGATE_SWITCH)
10747 ActivateTimegateSwitch(x, y);
10749 else if (element == EL_BALLOON_SWITCH_LEFT ||
10750 element == EL_BALLOON_SWITCH_RIGHT ||
10751 element == EL_BALLOON_SWITCH_UP ||
10752 element == EL_BALLOON_SWITCH_DOWN ||
10753 element == EL_BALLOON_SWITCH_NONE ||
10754 element == EL_BALLOON_SWITCH_ANY)
10756 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
10757 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10758 element == EL_BALLOON_SWITCH_UP ? MV_UP :
10759 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
10760 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
10763 else if (element == EL_LAMP)
10765 Feld[x][y] = EL_LAMP_ACTIVE;
10766 local_player->lights_still_needed--;
10768 ResetGfxAnimation(x, y);
10769 DrawLevelField(x, y);
10771 else if (element == EL_TIME_ORB_FULL)
10773 Feld[x][y] = EL_TIME_ORB_EMPTY;
10775 if (level.time > 0 || level.use_time_orb_bug)
10777 TimeLeft += level.time_orb_time;
10778 DrawGameValue_Time(TimeLeft);
10781 ResetGfxAnimation(x, y);
10782 DrawLevelField(x, y);
10784 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
10785 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10789 game.ball_state = !game.ball_state;
10792 SCAN_PLAYFIELD(xx, yy)
10794 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10797 int e = Feld[xx][yy];
10799 if (game.ball_state)
10801 if (e == EL_EMC_MAGIC_BALL)
10802 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
10803 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
10804 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
10808 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
10809 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
10810 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10811 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
10816 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10817 player->index_bit, dig_side);
10819 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10820 player->index_bit, dig_side);
10822 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10823 player->index_bit, dig_side);
10829 if (!PLAYER_SWITCHING(player, x, y))
10831 player->is_switching = TRUE;
10832 player->switch_x = x;
10833 player->switch_y = y;
10835 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10836 player->index_bit, dig_side);
10837 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10838 player->index_bit, dig_side);
10840 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
10841 player->index_bit, dig_side);
10842 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10843 player->index_bit, dig_side);
10846 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10847 player->index_bit, dig_side);
10848 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10849 player->index_bit, dig_side);
10851 return MF_NO_ACTION;
10854 player->push_delay = -1;
10856 if (is_player) /* function can also be called by EL_PENGUIN */
10858 if (Feld[x][y] != element) /* really digged/collected something */
10859 player->is_collecting = !player->is_digging;
10865 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10867 int jx = player->jx, jy = player->jy;
10868 int x = jx + dx, y = jy + dy;
10869 int snap_direction = (dx == -1 ? MV_LEFT :
10870 dx == +1 ? MV_RIGHT :
10872 dy == +1 ? MV_DOWN : MV_NONE);
10874 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10877 if (!player->active || !IN_LEV_FIELD(x, y))
10885 if (player->MovPos == 0)
10886 player->is_pushing = FALSE;
10888 player->is_snapping = FALSE;
10890 if (player->MovPos == 0)
10892 player->is_moving = FALSE;
10893 player->is_digging = FALSE;
10894 player->is_collecting = FALSE;
10900 if (player->is_snapping)
10903 player->MovDir = snap_direction;
10905 if (player->MovPos == 0)
10907 player->is_moving = FALSE;
10908 player->is_digging = FALSE;
10909 player->is_collecting = FALSE;
10912 player->is_dropping = FALSE;
10914 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10917 player->is_snapping = TRUE;
10919 if (player->MovPos == 0)
10921 player->is_moving = FALSE;
10922 player->is_digging = FALSE;
10923 player->is_collecting = FALSE;
10926 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
10927 DrawLevelField(player->last_jx, player->last_jy);
10929 DrawLevelField(x, y);
10934 boolean DropElement(struct PlayerInfo *player)
10936 int old_element, new_element;
10937 int dropx = player->jx, dropy = player->jy;
10938 int drop_direction = player->MovDir;
10939 int drop_side = drop_direction;
10940 int drop_element = (player->inventory_size > 0 ?
10941 player->inventory_element[player->inventory_size - 1] :
10942 player->inventory_infinite_element != EL_UNDEFINED ?
10943 player->inventory_infinite_element :
10944 player->dynabombs_left > 0 ?
10945 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10948 /* do not drop an element on top of another element; when holding drop key
10949 pressed without moving, dropped element must move away before the next
10950 element can be dropped (this is especially important if the next element
10951 is dynamite, which can be placed on background for historical reasons) */
10952 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10955 if (IS_THROWABLE(drop_element))
10957 dropx += GET_DX_FROM_DIR(drop_direction);
10958 dropy += GET_DY_FROM_DIR(drop_direction);
10960 if (!IN_LEV_FIELD(dropx, dropy))
10964 old_element = Feld[dropx][dropy]; /* old element at dropping position */
10965 new_element = drop_element; /* default: no change when dropping */
10967 /* check if player is active, not moving and ready to drop */
10968 if (!player->active || player->MovPos || player->drop_delay > 0)
10971 /* check if player has anything that can be dropped */
10972 if (new_element == EL_UNDEFINED)
10975 /* check if anything can be dropped at the current position */
10976 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10979 /* collected custom elements can only be dropped on empty fields */
10980 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10983 if (old_element != EL_EMPTY)
10984 Back[dropx][dropy] = old_element; /* store old element on this field */
10986 ResetGfxAnimation(dropx, dropy);
10987 ResetRandomAnimationValue(dropx, dropy);
10989 if (player->inventory_size > 0 ||
10990 player->inventory_infinite_element != EL_UNDEFINED)
10992 if (player->inventory_size > 0)
10994 player->inventory_size--;
10996 DrawGameValue_Dynamite(local_player->inventory_size);
10998 if (new_element == EL_DYNAMITE)
10999 new_element = EL_DYNAMITE_ACTIVE;
11000 else if (new_element == EL_SP_DISK_RED)
11001 new_element = EL_SP_DISK_RED_ACTIVE;
11004 Feld[dropx][dropy] = new_element;
11006 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11007 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11008 el2img(Feld[dropx][dropy]), 0);
11010 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11012 /* needed if previous element just changed to "empty" in the last frame */
11013 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11015 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11016 player->index_bit, drop_side);
11017 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11019 player->index_bit, drop_side);
11021 TestIfElementTouchesCustomElement(dropx, dropy);
11023 else /* player is dropping a dyna bomb */
11025 player->dynabombs_left--;
11027 Feld[dropx][dropy] = new_element;
11029 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11030 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11031 el2img(Feld[dropx][dropy]), 0);
11033 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11036 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11037 InitField_WithBug1(dropx, dropy, FALSE);
11039 new_element = Feld[dropx][dropy]; /* element might have changed */
11041 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11042 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11044 int move_direction, nextx, nexty;
11046 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11047 MovDir[dropx][dropy] = drop_direction;
11049 move_direction = MovDir[dropx][dropy];
11050 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11051 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11053 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11054 CheckCollision[dropx][dropy] = 2;
11057 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11058 player->is_dropping = TRUE;
11060 player->drop_x = dropx;
11061 player->drop_y = dropy;
11066 /* ------------------------------------------------------------------------- */
11067 /* game sound playing functions */
11068 /* ------------------------------------------------------------------------- */
11070 static int *loop_sound_frame = NULL;
11071 static int *loop_sound_volume = NULL;
11073 void InitPlayLevelSound()
11075 int num_sounds = getSoundListSize();
11077 checked_free(loop_sound_frame);
11078 checked_free(loop_sound_volume);
11080 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11081 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11084 static void PlayLevelSound(int x, int y, int nr)
11086 int sx = SCREENX(x), sy = SCREENY(y);
11087 int volume, stereo_position;
11088 int max_distance = 8;
11089 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11091 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11092 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11095 if (!IN_LEV_FIELD(x, y) ||
11096 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11097 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11100 volume = SOUND_MAX_VOLUME;
11102 if (!IN_SCR_FIELD(sx, sy))
11104 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11105 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11107 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11110 stereo_position = (SOUND_MAX_LEFT +
11111 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11112 (SCR_FIELDX + 2 * max_distance));
11114 if (IS_LOOP_SOUND(nr))
11116 /* This assures that quieter loop sounds do not overwrite louder ones,
11117 while restarting sound volume comparison with each new game frame. */
11119 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11122 loop_sound_volume[nr] = volume;
11123 loop_sound_frame[nr] = FrameCounter;
11126 PlaySoundExt(nr, volume, stereo_position, type);
11129 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11131 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11132 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11133 y < LEVELY(BY1) ? LEVELY(BY1) :
11134 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11138 static void PlayLevelSoundAction(int x, int y, int action)
11140 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11143 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11145 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11147 if (sound_effect != SND_UNDEFINED)
11148 PlayLevelSound(x, y, sound_effect);
11151 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11154 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11156 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11157 PlayLevelSound(x, y, sound_effect);
11160 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11162 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11164 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11165 PlayLevelSound(x, y, sound_effect);
11168 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11170 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11172 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11173 StopSound(sound_effect);
11176 static void PlayLevelMusic()
11178 if (levelset.music[level_nr] != MUS_UNDEFINED)
11179 PlayMusic(levelset.music[level_nr]); /* from config file */
11181 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11184 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11186 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11191 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11195 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11199 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11203 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11207 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11211 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11215 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11218 case SAMPLE_android_clone:
11219 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11222 case SAMPLE_android_move:
11223 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11226 case SAMPLE_spring:
11227 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11231 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
11235 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11238 case SAMPLE_eater_eat:
11239 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11243 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11246 case SAMPLE_collect:
11247 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11250 case SAMPLE_diamond:
11251 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11254 case SAMPLE_squash:
11255 /* !!! CHECK THIS !!! */
11257 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11259 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11263 case SAMPLE_wonderfall:
11264 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11268 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11272 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11276 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11280 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11284 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11288 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11291 case SAMPLE_wonder:
11292 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11296 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11299 case SAMPLE_exit_open:
11300 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11303 case SAMPLE_exit_leave:
11304 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11307 case SAMPLE_dynamite:
11308 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11312 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11316 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11320 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11324 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11328 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11332 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11336 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11341 void RaiseScore(int value)
11343 local_player->score += value;
11345 DrawGameValue_Score(local_player->score);
11348 void RaiseScoreElement(int element)
11353 case EL_BD_DIAMOND:
11354 case EL_EMERALD_YELLOW:
11355 case EL_EMERALD_RED:
11356 case EL_EMERALD_PURPLE:
11357 case EL_SP_INFOTRON:
11358 RaiseScore(level.score[SC_EMERALD]);
11361 RaiseScore(level.score[SC_DIAMOND]);
11364 RaiseScore(level.score[SC_CRYSTAL]);
11367 RaiseScore(level.score[SC_PEARL]);
11370 case EL_BD_BUTTERFLY:
11371 case EL_SP_ELECTRON:
11372 RaiseScore(level.score[SC_BUG]);
11375 case EL_BD_FIREFLY:
11376 case EL_SP_SNIKSNAK:
11377 RaiseScore(level.score[SC_SPACESHIP]);
11380 case EL_DARK_YAMYAM:
11381 RaiseScore(level.score[SC_YAMYAM]);
11384 RaiseScore(level.score[SC_ROBOT]);
11387 RaiseScore(level.score[SC_PACMAN]);
11390 RaiseScore(level.score[SC_NUT]);
11393 case EL_SP_DISK_RED:
11394 case EL_DYNABOMB_INCREASE_NUMBER:
11395 case EL_DYNABOMB_INCREASE_SIZE:
11396 case EL_DYNABOMB_INCREASE_POWER:
11397 RaiseScore(level.score[SC_DYNAMITE]);
11399 case EL_SHIELD_NORMAL:
11400 case EL_SHIELD_DEADLY:
11401 RaiseScore(level.score[SC_SHIELD]);
11403 case EL_EXTRA_TIME:
11404 RaiseScore(level.extra_time_score);
11418 RaiseScore(level.score[SC_KEY]);
11421 RaiseScore(element_info[element].collect_score);
11426 void RequestQuitGame(boolean ask_if_really_quit)
11428 if (AllPlayersGone ||
11429 !ask_if_really_quit ||
11430 level_editor_test_game ||
11431 Request("Do you really want to quit the game ?",
11432 REQ_ASK | REQ_STAY_CLOSED))
11434 #if defined(NETWORK_AVALIABLE)
11435 if (options.network)
11436 SendToServer_StopPlaying();
11440 game_status = GAME_MODE_MAIN;
11446 if (tape.playing && tape.deactivate_display)
11447 TapeDeactivateDisplayOff(TRUE);
11449 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11451 if (tape.playing && tape.deactivate_display)
11452 TapeDeactivateDisplayOn();
11457 /* ---------- new game button stuff ---------------------------------------- */
11459 /* graphic position values for game buttons */
11460 #define GAME_BUTTON_XSIZE 30
11461 #define GAME_BUTTON_YSIZE 30
11462 #define GAME_BUTTON_XPOS 5
11463 #define GAME_BUTTON_YPOS 215
11464 #define SOUND_BUTTON_XPOS 5
11465 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11467 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11468 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11469 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11470 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11471 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11472 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11479 } gamebutton_info[NUM_GAME_BUTTONS] =
11482 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11487 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11488 GAME_CTRL_ID_PAUSE,
11492 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11497 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11498 SOUND_CTRL_ID_MUSIC,
11499 "background music on/off"
11502 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11503 SOUND_CTRL_ID_LOOPS,
11504 "sound loops on/off"
11507 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11508 SOUND_CTRL_ID_SIMPLE,
11509 "normal sounds on/off"
11513 void CreateGameButtons()
11517 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11519 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11520 struct GadgetInfo *gi;
11523 unsigned long event_mask;
11524 int gd_xoffset, gd_yoffset;
11525 int gd_x1, gd_x2, gd_y1, gd_y2;
11528 gd_xoffset = gamebutton_info[i].x;
11529 gd_yoffset = gamebutton_info[i].y;
11530 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11531 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11533 if (id == GAME_CTRL_ID_STOP ||
11534 id == GAME_CTRL_ID_PAUSE ||
11535 id == GAME_CTRL_ID_PLAY)
11537 button_type = GD_TYPE_NORMAL_BUTTON;
11539 event_mask = GD_EVENT_RELEASED;
11540 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11541 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11545 button_type = GD_TYPE_CHECK_BUTTON;
11547 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11548 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11549 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11550 event_mask = GD_EVENT_PRESSED;
11551 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11552 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11555 gi = CreateGadget(GDI_CUSTOM_ID, id,
11556 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11557 GDI_X, DX + gd_xoffset,
11558 GDI_Y, DY + gd_yoffset,
11559 GDI_WIDTH, GAME_BUTTON_XSIZE,
11560 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11561 GDI_TYPE, button_type,
11562 GDI_STATE, GD_BUTTON_UNPRESSED,
11563 GDI_CHECKED, checked,
11564 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11565 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11566 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11567 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11568 GDI_EVENT_MASK, event_mask,
11569 GDI_CALLBACK_ACTION, HandleGameButtons,
11573 Error(ERR_EXIT, "cannot create gadget");
11575 game_gadget[id] = gi;
11579 void FreeGameButtons()
11583 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11584 FreeGadget(game_gadget[i]);
11587 static void MapGameButtons()
11591 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11592 MapGadget(game_gadget[i]);
11595 void UnmapGameButtons()
11599 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11600 UnmapGadget(game_gadget[i]);
11603 static void HandleGameButtons(struct GadgetInfo *gi)
11605 int id = gi->custom_id;
11607 if (game_status != GAME_MODE_PLAYING)
11612 case GAME_CTRL_ID_STOP:
11613 RequestQuitGame(TRUE);
11616 case GAME_CTRL_ID_PAUSE:
11617 if (options.network)
11619 #if defined(NETWORK_AVALIABLE)
11621 SendToServer_ContinuePlaying();
11623 SendToServer_PausePlaying();
11627 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11630 case GAME_CTRL_ID_PLAY:
11633 #if defined(NETWORK_AVALIABLE)
11634 if (options.network)
11635 SendToServer_ContinuePlaying();
11639 tape.pausing = FALSE;
11640 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11645 case SOUND_CTRL_ID_MUSIC:
11646 if (setup.sound_music)
11648 setup.sound_music = FALSE;
11651 else if (audio.music_available)
11653 setup.sound = setup.sound_music = TRUE;
11655 SetAudioMode(setup.sound);
11661 case SOUND_CTRL_ID_LOOPS:
11662 if (setup.sound_loops)
11663 setup.sound_loops = FALSE;
11664 else if (audio.loops_available)
11666 setup.sound = setup.sound_loops = TRUE;
11667 SetAudioMode(setup.sound);
11671 case SOUND_CTRL_ID_SIMPLE:
11672 if (setup.sound_simple)
11673 setup.sound_simple = FALSE;
11674 else if (audio.sound_available)
11676 setup.sound = setup.sound_simple = TRUE;
11677 SetAudioMode(setup.sound);