1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
47 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
54 /* for MovePlayer() */
55 #define MP_NO_ACTION 0
58 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
60 /* for ScrollPlayer() */
62 #define SCROLL_GO_ON 1
64 /* for Bang()/Explode() */
65 #define EX_PHASE_START 0
66 #define EX_TYPE_NONE 0
67 #define EX_TYPE_NORMAL (1 << 0)
68 #define EX_TYPE_CENTER (1 << 1)
69 #define EX_TYPE_BORDER (1 << 2)
70 #define EX_TYPE_CROSS (1 << 3)
71 #define EX_TYPE_DYNA (1 << 4)
72 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
74 /* special positions in the game control window (relative to control window) */
77 #define XX_EMERALDS 29
78 #define YY_EMERALDS 54
79 #define XX_DYNAMITE 29
80 #define YY_DYNAMITE 89
89 /* special positions in the game control window (relative to main window) */
90 #define DX_LEVEL (DX + XX_LEVEL)
91 #define DY_LEVEL (DY + YY_LEVEL)
92 #define DX_EMERALDS (DX + XX_EMERALDS)
93 #define DY_EMERALDS (DY + YY_EMERALDS)
94 #define DX_DYNAMITE (DX + XX_DYNAMITE)
95 #define DY_DYNAMITE (DY + YY_DYNAMITE)
96 #define DX_KEYS (DX + XX_KEYS)
97 #define DY_KEYS (DY + YY_KEYS)
98 #define DX_SCORE (DX + XX_SCORE)
99 #define DY_SCORE (DY + YY_SCORE)
100 #define DX_TIME1 (DX + XX_TIME1)
101 #define DX_TIME2 (DX + XX_TIME2)
102 #define DY_TIME (DY + YY_TIME)
104 /* values for delayed check of falling and moving elements and for collision */
105 #define CHECK_DELAY_MOVING 3
106 #define CHECK_DELAY_FALLING 3
107 #define CHECK_DELAY_COLLISION 2
109 /* values for initial player move delay (initial delay counter value) */
110 #define INITIAL_MOVE_DELAY_OFF -1
111 #define INITIAL_MOVE_DELAY_ON 0
113 /* values for player movement speed (which is in fact a delay value) */
114 #define MOVE_DELAY_MIN_SPEED 32
115 #define MOVE_DELAY_NORMAL_SPEED 8
116 #define MOVE_DELAY_HIGH_SPEED 4
117 #define MOVE_DELAY_MAX_SPEED 1
120 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
121 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
123 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
124 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
126 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
127 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
129 /* values for other actions */
130 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
131 #define MOVE_STEPSIZE_MIN (1)
132 #define MOVE_STEPSIZE_MAX (TILEX)
134 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
135 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
137 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
139 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
140 RND(element_info[e].push_delay_random))
141 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
142 RND(element_info[e].drop_delay_random))
143 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
144 RND(element_info[e].move_delay_random))
145 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
146 (element_info[e].move_delay_random))
147 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
148 RND(element_info[e].ce_value_random_initial))
149 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
150 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
151 RND((c)->delay_random * (c)->delay_frames))
152 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
153 RND((c)->delay_random))
157 #define GET_VALID_RUNTIME_ELEMENT(e) \
158 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
160 #define GET_VALID_FILE_ELEMENT(e) \
161 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
164 #define GET_TARGET_ELEMENT(e, ch, cv, cs) \
165 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
166 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
167 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
168 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
169 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
170 (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
172 #define CAN_GROW_INTO(e) \
173 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
175 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
176 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
179 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
180 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
181 (CAN_MOVE_INTO_ACID(e) && \
182 Feld[x][y] == EL_ACID) || \
185 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
186 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
187 (CAN_MOVE_INTO_ACID(e) && \
188 Feld[x][y] == EL_ACID) || \
191 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
192 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
194 (CAN_MOVE_INTO_ACID(e) && \
195 Feld[x][y] == EL_ACID) || \
196 (DONT_COLLIDE_WITH(e) && \
198 !PLAYER_ENEMY_PROTECTED(x, y))))
200 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
203 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
204 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
206 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
209 #define ANDROID_CAN_CLONE_FIELD(x, y) \
210 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
211 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
213 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
214 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
216 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
219 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
220 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
222 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
223 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
225 #define PIG_CAN_ENTER_FIELD(e, x, y) \
226 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
228 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
229 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
230 IS_FOOD_PENGUIN(Feld[x][y])))
231 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
234 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
237 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
240 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
241 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
242 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
245 #define GROUP_NR(e) ((e) - EL_GROUP_START)
246 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
247 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
249 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
250 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
253 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
255 #define CE_ENTER_FIELD_COND(e, x, y) \
256 (!IS_PLAYER(x, y) && \
257 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
259 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
260 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
262 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
263 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
265 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
266 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
267 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
268 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
270 /* game button identifiers */
271 #define GAME_CTRL_ID_STOP 0
272 #define GAME_CTRL_ID_PAUSE 1
273 #define GAME_CTRL_ID_PLAY 2
274 #define SOUND_CTRL_ID_MUSIC 3
275 #define SOUND_CTRL_ID_LOOPS 4
276 #define SOUND_CTRL_ID_SIMPLE 5
278 #define NUM_GAME_BUTTONS 6
281 /* forward declaration for internal use */
283 static void CreateField(int, int, int);
285 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
286 static void AdvanceFrameAndPlayerCounters(int);
288 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
289 static boolean MovePlayer(struct PlayerInfo *, int, int);
290 static void ScrollPlayer(struct PlayerInfo *, int);
291 static void ScrollScreen(struct PlayerInfo *, int);
293 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
295 static void InitBeltMovement(void);
296 static void CloseAllOpenTimegates(void);
297 static void CheckGravityMovement(struct PlayerInfo *);
298 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
299 static void KillPlayerUnlessEnemyProtected(int, int);
300 static void KillPlayerUnlessExplosionProtected(int, int);
302 static void TestIfPlayerTouchesCustomElement(int, int);
303 static void TestIfElementTouchesCustomElement(int, int);
304 static void TestIfElementHitsCustomElement(int, int, int);
306 static void TestIfElementSmashesCustomElement(int, int, int);
309 static void HandleElementChange(int, int, int);
310 static void ExecuteCustomElementAction(int, int, int, int);
311 static boolean ChangeElement(int, int, int, int);
313 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
314 #define CheckTriggeredElementChange(x, y, e, ev) \
315 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
316 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
317 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
318 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
319 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
320 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
321 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
323 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
324 #define CheckElementChange(x, y, e, te, ev) \
325 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
326 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
327 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
328 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
329 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
331 static void PlayLevelSound(int, int, int);
332 static void PlayLevelSoundNearest(int, int, int);
333 static void PlayLevelSoundAction(int, int, int);
334 static void PlayLevelSoundElementAction(int, int, int, int);
335 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
336 static void PlayLevelSoundActionIfLoop(int, int, int);
337 static void StopLevelSoundActionIfLoop(int, int, int);
338 static void PlayLevelMusic();
340 static void MapGameButtons();
341 static void HandleGameButtons(struct GadgetInfo *);
343 int AmoebeNachbarNr(int, int);
344 void AmoebeUmwandeln(int, int);
345 void ContinueMoving(int, int);
347 void InitMovDir(int, int);
348 void InitAmoebaNr(int, int);
349 int NewHiScore(void);
351 void TestIfGoodThingHitsBadThing(int, int, int);
352 void TestIfBadThingHitsGoodThing(int, int, int);
353 void TestIfPlayerTouchesBadThing(int, int);
354 void TestIfPlayerRunsIntoBadThing(int, int, int);
355 void TestIfBadThingTouchesPlayer(int, int);
356 void TestIfBadThingRunsIntoPlayer(int, int, int);
357 void TestIfFriendTouchesBadThing(int, int);
358 void TestIfBadThingTouchesFriend(int, int);
359 void TestIfBadThingTouchesOtherBadThing(int, int);
361 void KillPlayer(struct PlayerInfo *);
362 void BuryPlayer(struct PlayerInfo *);
363 void RemovePlayer(struct PlayerInfo *);
365 boolean SnapField(struct PlayerInfo *, int, int);
366 boolean DropElement(struct PlayerInfo *);
368 static int getInvisibleActiveFromInvisibleElement(int);
369 static int getInvisibleFromInvisibleActiveElement(int);
371 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
374 /* ------------------------------------------------------------------------- */
375 /* definition of elements that automatically change to other elements after */
376 /* a specified time, eventually calling a function when changing */
377 /* ------------------------------------------------------------------------- */
379 /* forward declaration for changer functions */
380 static void InitBuggyBase(int, int);
381 static void WarnBuggyBase(int, int);
383 static void InitTrap(int, int);
384 static void ActivateTrap(int, int);
385 static void ChangeActiveTrap(int, int);
387 static void InitRobotWheel(int, int);
388 static void RunRobotWheel(int, int);
389 static void StopRobotWheel(int, int);
391 static void InitTimegateWheel(int, int);
392 static void RunTimegateWheel(int, int);
394 static void InitMagicBallDelay(int, int);
395 static void ActivateMagicBall(int, int);
397 static void InitDiagonalMovingElement(int, int);
399 struct ChangingElementInfo
404 void (*pre_change_function)(int x, int y);
405 void (*change_function)(int x, int y);
406 void (*post_change_function)(int x, int y);
409 static struct ChangingElementInfo change_delay_list[] =
460 EL_SWITCHGATE_OPENING,
468 EL_SWITCHGATE_CLOSING,
469 EL_SWITCHGATE_CLOSED,
501 EL_ACID_SPLASH_RIGHT,
510 EL_SP_BUGGY_BASE_ACTIVATING,
517 EL_SP_BUGGY_BASE_ACTIVATING,
518 EL_SP_BUGGY_BASE_ACTIVE,
525 EL_SP_BUGGY_BASE_ACTIVE,
549 EL_ROBOT_WHEEL_ACTIVE,
557 EL_TIMEGATE_SWITCH_ACTIVE,
565 EL_EMC_MAGIC_BALL_ACTIVE,
566 EL_EMC_MAGIC_BALL_ACTIVE,
573 EL_EMC_SPRING_BUMPER_ACTIVE,
574 EL_EMC_SPRING_BUMPER,
581 EL_DIAGONAL_SHRINKING,
594 InitDiagonalMovingElement
610 int push_delay_fixed, push_delay_random;
615 { EL_BALLOON, 0, 0 },
617 { EL_SOKOBAN_OBJECT, 2, 0 },
618 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
619 { EL_SATELLITE, 2, 0 },
620 { EL_SP_DISK_YELLOW, 2, 0 },
622 { EL_UNDEFINED, 0, 0 },
630 move_stepsize_list[] =
632 { EL_AMOEBA_DROP, 2 },
633 { EL_AMOEBA_DROPPING, 2 },
634 { EL_QUICKSAND_FILLING, 1 },
635 { EL_QUICKSAND_EMPTYING, 1 },
636 { EL_MAGIC_WALL_FILLING, 2 },
637 { EL_BD_MAGIC_WALL_FILLING, 2 },
638 { EL_MAGIC_WALL_EMPTYING, 2 },
639 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
649 collect_count_list[] =
652 { EL_BD_DIAMOND, 1 },
653 { EL_EMERALD_YELLOW, 1 },
654 { EL_EMERALD_RED, 1 },
655 { EL_EMERALD_PURPLE, 1 },
657 { EL_SP_INFOTRON, 1 },
669 access_direction_list[] =
671 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
672 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
673 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
674 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
675 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
676 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
677 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
678 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
679 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
680 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
681 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
683 { EL_SP_PORT_LEFT, MV_RIGHT },
684 { EL_SP_PORT_RIGHT, MV_LEFT },
685 { EL_SP_PORT_UP, MV_DOWN },
686 { EL_SP_PORT_DOWN, MV_UP },
687 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
688 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
689 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
690 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
691 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
692 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
693 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
694 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
695 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
696 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
697 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
698 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
699 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
700 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
701 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
703 { EL_UNDEFINED, MV_NONE }
706 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
708 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
709 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
710 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
711 IS_JUST_CHANGING(x, y))
713 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
715 /* static variables for playfield scan mode (scanning forward or backward) */
716 static int playfield_scan_start_x = 0;
717 static int playfield_scan_start_y = 0;
718 static int playfield_scan_delta_x = 1;
719 static int playfield_scan_delta_y = 1;
721 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
722 (y) >= 0 && (y) <= lev_fieldy - 1; \
723 (y) += playfield_scan_delta_y) \
724 for ((x) = playfield_scan_start_x; \
725 (x) >= 0 && (x) <= lev_fieldx - 1; \
726 (x) += playfield_scan_delta_x) \
729 void DEBUG_SetMaximumDynamite()
733 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
734 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
735 local_player->inventory_element[local_player->inventory_size++] =
740 static void InitPlayfieldScanModeVars()
742 if (game.use_reverse_scan_direction)
744 playfield_scan_start_x = lev_fieldx - 1;
745 playfield_scan_start_y = lev_fieldy - 1;
747 playfield_scan_delta_x = -1;
748 playfield_scan_delta_y = -1;
752 playfield_scan_start_x = 0;
753 playfield_scan_start_y = 0;
755 playfield_scan_delta_x = 1;
756 playfield_scan_delta_y = 1;
760 static void InitPlayfieldScanMode(int mode)
762 game.use_reverse_scan_direction =
763 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
765 InitPlayfieldScanModeVars();
768 static int get_move_delay_from_stepsize(int move_stepsize)
771 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
773 /* make sure that stepsize value is always a power of 2 */
774 move_stepsize = (1 << log_2(move_stepsize));
776 return TILEX / move_stepsize;
779 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
782 int move_delay = get_move_delay_from_stepsize(move_stepsize);
783 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
785 /* do no immediately change move delay -- the player might just be moving */
786 player->move_delay_value_next = move_delay;
788 /* information if player can move must be set separately */
789 player->cannot_move = cannot_move;
793 player->move_delay = game.initial_move_delay;
794 player->move_delay_value = game.initial_move_delay_value;
796 player->move_delay_value_next = -1;
798 player->move_delay_reset_counter = 0;
802 void GetPlayerConfig()
804 if (!audio.sound_available)
805 setup.sound_simple = FALSE;
807 if (!audio.loops_available)
808 setup.sound_loops = FALSE;
810 if (!audio.music_available)
811 setup.sound_music = FALSE;
813 if (!video.fullscreen_available)
814 setup.fullscreen = FALSE;
816 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
818 SetAudioMode(setup.sound);
822 static int getBeltNrFromBeltElement(int element)
824 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
825 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
826 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
829 static int getBeltNrFromBeltActiveElement(int element)
831 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
832 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
833 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
836 static int getBeltNrFromBeltSwitchElement(int element)
838 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
839 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
840 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
843 static int getBeltDirNrFromBeltSwitchElement(int element)
845 static int belt_base_element[4] =
847 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
848 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
849 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
850 EL_CONVEYOR_BELT_4_SWITCH_LEFT
853 int belt_nr = getBeltNrFromBeltSwitchElement(element);
854 int belt_dir_nr = element - belt_base_element[belt_nr];
856 return (belt_dir_nr % 3);
859 static int getBeltDirFromBeltSwitchElement(int element)
861 static int belt_move_dir[3] =
868 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
870 return belt_move_dir[belt_dir_nr];
873 static int get_element_from_group_element(int element)
875 if (IS_GROUP_ELEMENT(element))
877 struct ElementGroupInfo *group = element_info[element].group;
878 int last_anim_random_frame = gfx.anim_random_frame;
881 if (group->choice_mode == ANIM_RANDOM)
882 gfx.anim_random_frame = RND(group->num_elements_resolved);
884 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
885 group->choice_mode, 0,
888 if (group->choice_mode == ANIM_RANDOM)
889 gfx.anim_random_frame = last_anim_random_frame;
893 element = group->element_resolved[element_pos];
899 static void InitPlayerField(int x, int y, int element, boolean init_game)
901 if (element == EL_SP_MURPHY)
905 if (stored_player[0].present)
907 Feld[x][y] = EL_SP_MURPHY_CLONE;
913 stored_player[0].use_murphy = TRUE;
915 if (!level.use_artwork_element[0])
916 stored_player[0].artwork_element = EL_SP_MURPHY;
919 Feld[x][y] = EL_PLAYER_1;
925 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
926 int jx = player->jx, jy = player->jy;
928 player->present = TRUE;
930 player->block_last_field = (element == EL_SP_MURPHY ?
931 level.sp_block_last_field :
932 level.block_last_field);
934 /* ---------- initialize player's last field block delay --------------- */
936 /* always start with reliable default value (no adjustment needed) */
937 player->block_delay_adjustment = 0;
939 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
940 if (player->block_last_field && element == EL_SP_MURPHY)
941 player->block_delay_adjustment = 1;
943 /* special case 2: in game engines before 3.1.1, blocking was different */
944 if (game.use_block_last_field_bug)
945 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
947 if (!options.network || player->connected)
949 player->active = TRUE;
951 /* remove potentially duplicate players */
952 if (StorePlayer[jx][jy] == Feld[x][y])
953 StorePlayer[jx][jy] = 0;
955 StorePlayer[x][y] = Feld[x][y];
959 printf("Player %d activated.\n", player->element_nr);
960 printf("[Local player is %d and currently %s.]\n",
961 local_player->element_nr,
962 local_player->active ? "active" : "not active");
966 Feld[x][y] = EL_EMPTY;
968 player->jx = player->last_jx = x;
969 player->jy = player->last_jy = y;
973 static void InitField(int x, int y, boolean init_game)
975 int element = Feld[x][y];
984 InitPlayerField(x, y, element, init_game);
987 case EL_SOKOBAN_FIELD_PLAYER:
988 element = Feld[x][y] = EL_PLAYER_1;
989 InitField(x, y, init_game);
991 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
992 InitField(x, y, init_game);
995 case EL_SOKOBAN_FIELD_EMPTY:
996 local_player->sokobanfields_still_needed++;
1000 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1001 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1002 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1003 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1004 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1005 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1006 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1007 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1008 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1009 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1018 case EL_SPACESHIP_RIGHT:
1019 case EL_SPACESHIP_UP:
1020 case EL_SPACESHIP_LEFT:
1021 case EL_SPACESHIP_DOWN:
1022 case EL_BD_BUTTERFLY:
1023 case EL_BD_BUTTERFLY_RIGHT:
1024 case EL_BD_BUTTERFLY_UP:
1025 case EL_BD_BUTTERFLY_LEFT:
1026 case EL_BD_BUTTERFLY_DOWN:
1028 case EL_BD_FIREFLY_RIGHT:
1029 case EL_BD_FIREFLY_UP:
1030 case EL_BD_FIREFLY_LEFT:
1031 case EL_BD_FIREFLY_DOWN:
1032 case EL_PACMAN_RIGHT:
1034 case EL_PACMAN_LEFT:
1035 case EL_PACMAN_DOWN:
1037 case EL_YAMYAM_LEFT:
1038 case EL_YAMYAM_RIGHT:
1040 case EL_YAMYAM_DOWN:
1041 case EL_DARK_YAMYAM:
1044 case EL_SP_SNIKSNAK:
1045 case EL_SP_ELECTRON:
1054 case EL_AMOEBA_FULL:
1059 case EL_AMOEBA_DROP:
1060 if (y == lev_fieldy - 1)
1062 Feld[x][y] = EL_AMOEBA_GROWING;
1063 Store[x][y] = EL_AMOEBA_WET;
1067 case EL_DYNAMITE_ACTIVE:
1068 case EL_SP_DISK_RED_ACTIVE:
1069 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1070 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1071 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1072 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1073 MovDelay[x][y] = 96;
1076 case EL_EM_DYNAMITE_ACTIVE:
1077 MovDelay[x][y] = 32;
1081 local_player->lights_still_needed++;
1085 local_player->friends_still_needed++;
1090 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1093 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1094 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1095 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1096 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1097 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1098 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1099 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1100 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1101 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1102 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1103 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1104 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1107 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1108 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1109 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1111 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1113 game.belt_dir[belt_nr] = belt_dir;
1114 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1116 else /* more than one switch -- set it like the first switch */
1118 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1123 #if !USE_BOTH_SWITCHGATE_SWITCHES
1124 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1126 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1130 case EL_LIGHT_SWITCH_ACTIVE:
1132 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1135 case EL_INVISIBLE_STEELWALL:
1136 case EL_INVISIBLE_WALL:
1137 case EL_INVISIBLE_SAND:
1138 if (game.light_time_left > 0 ||
1139 game.lenses_time_left > 0)
1140 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1143 case EL_EMC_MAGIC_BALL:
1144 if (game.ball_state)
1145 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1148 case EL_EMC_MAGIC_BALL_SWITCH:
1149 if (game.ball_state)
1150 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1155 if (IS_CUSTOM_ELEMENT(element))
1157 if (CAN_MOVE(element))
1160 #if USE_NEW_CUSTOM_VALUE
1161 if (!element_info[element].use_last_ce_value || init_game)
1162 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1166 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1169 else if (IS_GROUP_ELEMENT(element))
1172 Feld[x][y] = get_element_from_group_element(element);
1174 InitField(x, y, init_game);
1176 struct ElementGroupInfo *group = element_info[element].group;
1177 int last_anim_random_frame = gfx.anim_random_frame;
1180 if (group->choice_mode == ANIM_RANDOM)
1181 gfx.anim_random_frame = RND(group->num_elements_resolved);
1183 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1184 group->choice_mode, 0,
1187 if (group->choice_mode == ANIM_RANDOM)
1188 gfx.anim_random_frame = last_anim_random_frame;
1190 group->choice_pos++;
1192 Feld[x][y] = group->element_resolved[element_pos];
1194 InitField(x, y, init_game);
1202 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1207 #if USE_NEW_CUSTOM_VALUE
1210 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1212 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1220 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1222 InitField(x, y, init_game);
1224 /* not needed to call InitMovDir() -- already done by InitField()! */
1225 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1226 CAN_MOVE(Feld[x][y]))
1230 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1232 int old_element = Feld[x][y];
1234 InitField(x, y, init_game);
1236 /* not needed to call InitMovDir() -- already done by InitField()! */
1237 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1238 CAN_MOVE(old_element) &&
1239 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1242 /* this case is in fact a combination of not less than three bugs:
1243 first, it calls InitMovDir() for elements that can move, although this is
1244 already done by InitField(); then, it checks the element that was at this
1245 field _before_ the call to InitField() (which can change it); lastly, it
1246 was not called for "mole with direction" elements, which were treated as
1247 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1251 inline void DrawGameValue_Emeralds(int value)
1253 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1255 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1258 inline void DrawGameValue_Dynamite(int value)
1260 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1262 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1265 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1267 int base_key_graphic = EL_KEY_1;
1270 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1271 base_key_graphic = EL_EM_KEY_1;
1273 /* currently only 4 of 8 possible keys are displayed */
1274 for (i = 0; i < STD_NUM_KEYS; i++)
1277 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1278 el2edimg(base_key_graphic + i));
1280 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1281 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1282 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1286 inline void DrawGameValue_Score(int value)
1288 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1290 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1293 inline void DrawGameValue_Time(int value)
1295 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1296 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1298 /* clear background if value just changed its size */
1299 if (value == 999 || value == 1000)
1300 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1303 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1305 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1308 inline void DrawGameValue_Level(int value)
1311 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1314 /* misuse area for displaying emeralds to draw bigger level number */
1315 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1316 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1318 /* now copy it to the area for displaying level number */
1319 BlitBitmap(drawto, drawto,
1320 DX_EMERALDS, DY_EMERALDS + 1,
1321 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1322 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1323 DX_LEVEL - 1, DY_LEVEL + 1);
1325 /* restore the area for displaying emeralds */
1326 DrawGameValue_Emeralds(local_player->gems_still_needed);
1328 /* yes, this is all really ugly :-) */
1332 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1335 int key[MAX_NUM_KEYS];
1338 for (i = 0; i < MAX_NUM_KEYS; i++)
1339 key[i] = key_bits & (1 << i);
1341 DrawGameValue_Level(level_nr);
1343 DrawGameValue_Emeralds(emeralds);
1344 DrawGameValue_Dynamite(dynamite);
1345 DrawGameValue_Score(score);
1346 DrawGameValue_Time(time);
1348 DrawGameValue_Keys(key);
1351 void DrawGameDoorValues()
1353 int dynamite_state = 0;
1357 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1359 DrawGameDoorValues_EM();
1365 DrawGameValue_Level(level_nr);
1367 DrawGameValue_Emeralds(local_player->gems_still_needed);
1368 DrawGameValue_Dynamite(local_player->inventory_size);
1369 DrawGameValue_Score(local_player->score);
1370 DrawGameValue_Time(TimeLeft);
1374 if (game.centered_player_nr == -1)
1376 for (i = 0; i < MAX_PLAYERS; i++)
1378 for (j = 0; j < MAX_NUM_KEYS; j++)
1379 if (stored_player[i].key[j])
1380 key_bits |= (1 << j);
1382 dynamite_state += stored_player[i].inventory_size;
1386 DrawGameValue_Keys(stored_player[i].key);
1391 int player_nr = game.centered_player_nr;
1393 for (i = 0; i < MAX_NUM_KEYS; i++)
1394 if (stored_player[player_nr].key[i])
1395 key_bits |= (1 << i);
1397 dynamite_state = stored_player[player_nr].inventory_size;
1400 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1401 local_player->score, TimeLeft, key_bits);
1406 static void resolve_group_element(int group_element, int recursion_depth)
1408 static int group_nr;
1409 static struct ElementGroupInfo *group;
1410 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1413 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1415 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1416 group_element - EL_GROUP_START + 1);
1418 /* replace element which caused too deep recursion by question mark */
1419 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1424 if (recursion_depth == 0) /* initialization */
1426 group = element_info[group_element].group;
1427 group_nr = group_element - EL_GROUP_START;
1429 group->num_elements_resolved = 0;
1430 group->choice_pos = 0;
1433 for (i = 0; i < actual_group->num_elements; i++)
1435 int element = actual_group->element[i];
1437 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1440 if (IS_GROUP_ELEMENT(element))
1441 resolve_group_element(element, recursion_depth + 1);
1444 group->element_resolved[group->num_elements_resolved++] = element;
1445 element_info[element].in_group[group_nr] = TRUE;
1452 =============================================================================
1454 -----------------------------------------------------------------------------
1455 initialize game engine due to level / tape version number
1456 =============================================================================
1459 static void InitGameEngine()
1461 int i, j, k, l, x, y;
1463 /* set game engine from tape file when re-playing, else from level file */
1464 game.engine_version = (tape.playing ? tape.engine_version :
1465 level.game_version);
1467 /* ---------------------------------------------------------------------- */
1468 /* set flags for bugs and changes according to active game engine version */
1469 /* ---------------------------------------------------------------------- */
1472 Summary of bugfix/change:
1473 Fixed handling for custom elements that change when pushed by the player.
1475 Fixed/changed in version:
1479 Before 3.1.0, custom elements that "change when pushing" changed directly
1480 after the player started pushing them (until then handled in "DigField()").
1481 Since 3.1.0, these custom elements are not changed until the "pushing"
1482 move of the element is finished (now handled in "ContinueMoving()").
1484 Affected levels/tapes:
1485 The first condition is generally needed for all levels/tapes before version
1486 3.1.0, which might use the old behaviour before it was changed; known tapes
1487 that are affected are some tapes from the level set "Walpurgis Gardens" by
1489 The second condition is an exception from the above case and is needed for
1490 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1491 above (including some development versions of 3.1.0), but before it was
1492 known that this change would break tapes like the above and was fixed in
1493 3.1.1, so that the changed behaviour was active although the engine version
1494 while recording maybe was before 3.1.0. There is at least one tape that is
1495 affected by this exception, which is the tape for the one-level set "Bug
1496 Machine" by Juergen Bonhagen.
1499 game.use_change_when_pushing_bug =
1500 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1502 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1503 tape.game_version < VERSION_IDENT(3,1,1,0)));
1506 Summary of bugfix/change:
1507 Fixed handling for blocking the field the player leaves when moving.
1509 Fixed/changed in version:
1513 Before 3.1.1, when "block last field when moving" was enabled, the field
1514 the player is leaving when moving was blocked for the time of the move,
1515 and was directly unblocked afterwards. This resulted in the last field
1516 being blocked for exactly one less than the number of frames of one player
1517 move. Additionally, even when blocking was disabled, the last field was
1518 blocked for exactly one frame.
1519 Since 3.1.1, due to changes in player movement handling, the last field
1520 is not blocked at all when blocking is disabled. When blocking is enabled,
1521 the last field is blocked for exactly the number of frames of one player
1522 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1523 last field is blocked for exactly one more than the number of frames of
1526 Affected levels/tapes:
1527 (!!! yet to be determined -- probably many !!!)
1530 game.use_block_last_field_bug =
1531 (game.engine_version < VERSION_IDENT(3,1,1,0));
1534 Summary of bugfix/change:
1535 Changed behaviour of CE changes with multiple changes per single frame.
1537 Fixed/changed in version:
1541 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1542 This resulted in race conditions where CEs seem to behave strange in some
1543 situations (where triggered CE changes were just skipped because there was
1544 already a CE change on that tile in the playfield in that engine frame).
1545 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1546 (The number of changes per frame must be limited in any case, because else
1547 it is easily possible to define CE changes that would result in an infinite
1548 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1549 should be set large enough so that it would only be reached in cases where
1550 the corresponding CE change conditions run into a loop. Therefore, it seems
1551 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1552 maximal number of change pages for custom elements.)
1554 Affected levels/tapes:
1558 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1559 game.max_num_changes_per_frame = 1;
1561 game.max_num_changes_per_frame =
1562 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1565 /* ---------------------------------------------------------------------- */
1567 /* default scan direction: scan playfield from top/left to bottom/right */
1568 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1570 /* dynamically adjust element properties according to game engine version */
1571 InitElementPropertiesEngine(game.engine_version);
1574 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1575 printf(" tape version == %06d [%s] [file: %06d]\n",
1576 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1578 printf(" => game.engine_version == %06d\n", game.engine_version);
1582 /* ---------- recursively resolve group elements ------------------------- */
1584 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1585 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1586 element_info[i].in_group[j] = FALSE;
1588 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1589 resolve_group_element(EL_GROUP_START + i, 0);
1592 /* ---------- initialize player's initial move delay --------------------- */
1595 /* dynamically adjust player properties according to level information */
1596 game.initial_move_delay_value =
1597 get_move_delay_from_stepsize(level.initial_player_stepsize);
1599 /* dynamically adjust player properties according to level information */
1600 game.initial_move_delay_value =
1601 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1604 /* dynamically adjust player properties according to game engine version */
1605 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1606 game.initial_move_delay_value : 0);
1608 /* ---------- initialize player's initial push delay --------------------- */
1610 /* dynamically adjust player properties according to game engine version */
1611 game.initial_push_delay_value =
1612 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1614 /* ---------- initialize changing elements ------------------------------- */
1616 /* initialize changing elements information */
1617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1619 struct ElementInfo *ei = &element_info[i];
1621 /* this pointer might have been changed in the level editor */
1622 ei->change = &ei->change_page[0];
1624 if (!IS_CUSTOM_ELEMENT(i))
1626 ei->change->target_element = EL_EMPTY_SPACE;
1627 ei->change->delay_fixed = 0;
1628 ei->change->delay_random = 0;
1629 ei->change->delay_frames = 1;
1632 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1634 ei->has_change_event[j] = FALSE;
1636 ei->event_page_nr[j] = 0;
1637 ei->event_page[j] = &ei->change_page[0];
1641 /* add changing elements from pre-defined list */
1642 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1644 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1645 struct ElementInfo *ei = &element_info[ch_delay->element];
1647 ei->change->target_element = ch_delay->target_element;
1648 ei->change->delay_fixed = ch_delay->change_delay;
1650 ei->change->pre_change_function = ch_delay->pre_change_function;
1651 ei->change->change_function = ch_delay->change_function;
1652 ei->change->post_change_function = ch_delay->post_change_function;
1654 ei->change->can_change = TRUE;
1655 ei->change->can_change_or_has_action = TRUE;
1657 ei->has_change_event[CE_DELAY] = TRUE;
1659 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1660 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1663 /* ---------- initialize internal run-time variables ------------- */
1665 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1667 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1669 for (j = 0; j < ei->num_change_pages; j++)
1671 ei->change_page[j].can_change_or_has_action =
1672 (ei->change_page[j].can_change |
1673 ei->change_page[j].has_action);
1677 /* add change events from custom element configuration */
1678 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1680 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1682 for (j = 0; j < ei->num_change_pages; j++)
1684 if (!ei->change_page[j].can_change_or_has_action)
1687 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1689 /* only add event page for the first page found with this event */
1690 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1692 ei->has_change_event[k] = TRUE;
1694 ei->event_page_nr[k] = j;
1695 ei->event_page[k] = &ei->change_page[j];
1701 /* ---------- initialize run-time trigger player and element ------------- */
1703 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1705 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1707 for (j = 0; j < ei->num_change_pages; j++)
1709 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1710 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1711 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1712 ei->change_page[j].actual_trigger_ce_value = 0;
1713 ei->change_page[j].actual_trigger_ce_score = 0;
1717 /* ---------- initialize trigger events ---------------------------------- */
1719 /* initialize trigger events information */
1720 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1721 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1722 trigger_events[i][j] = FALSE;
1724 /* add trigger events from element change event properties */
1725 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1727 struct ElementInfo *ei = &element_info[i];
1729 for (j = 0; j < ei->num_change_pages; j++)
1731 if (!ei->change_page[j].can_change_or_has_action)
1734 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1736 int trigger_element = ei->change_page[j].trigger_element;
1738 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1740 if (ei->change_page[j].has_event[k])
1742 if (IS_GROUP_ELEMENT(trigger_element))
1744 struct ElementGroupInfo *group =
1745 element_info[trigger_element].group;
1747 for (l = 0; l < group->num_elements_resolved; l++)
1748 trigger_events[group->element_resolved[l]][k] = TRUE;
1751 trigger_events[trigger_element][k] = TRUE;
1758 /* ---------- initialize push delay -------------------------------------- */
1760 /* initialize push delay values to default */
1761 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1763 if (!IS_CUSTOM_ELEMENT(i))
1765 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1766 element_info[i].push_delay_random = game.default_push_delay_random;
1770 /* set push delay value for certain elements from pre-defined list */
1771 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1773 int e = push_delay_list[i].element;
1775 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1776 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1779 /* set push delay value for Supaplex elements for newer engine versions */
1780 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1782 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1784 if (IS_SP_ELEMENT(i))
1786 /* set SP push delay to just enough to push under a falling zonk */
1787 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1789 element_info[i].push_delay_fixed = delay;
1790 element_info[i].push_delay_random = 0;
1795 /* ---------- initialize move stepsize ----------------------------------- */
1797 /* initialize move stepsize values to default */
1798 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1799 if (!IS_CUSTOM_ELEMENT(i))
1800 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1802 /* set move stepsize value for certain elements from pre-defined list */
1803 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1805 int e = move_stepsize_list[i].element;
1807 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1810 /* ---------- initialize collect score ----------------------------------- */
1812 /* initialize collect score values for custom elements from initial value */
1813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1814 if (IS_CUSTOM_ELEMENT(i))
1815 element_info[i].collect_score = element_info[i].collect_score_initial;
1817 /* ---------- initialize collect count ----------------------------------- */
1819 /* initialize collect count values for non-custom elements */
1820 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1821 if (!IS_CUSTOM_ELEMENT(i))
1822 element_info[i].collect_count_initial = 0;
1824 /* add collect count values for all elements from pre-defined list */
1825 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1826 element_info[collect_count_list[i].element].collect_count_initial =
1827 collect_count_list[i].count;
1829 /* ---------- initialize access direction -------------------------------- */
1831 /* initialize access direction values to default (access from every side) */
1832 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1833 if (!IS_CUSTOM_ELEMENT(i))
1834 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1836 /* set access direction value for certain elements from pre-defined list */
1837 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1838 element_info[access_direction_list[i].element].access_direction =
1839 access_direction_list[i].direction;
1841 /* ---------- initialize explosion content ------------------------------- */
1842 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1844 if (IS_CUSTOM_ELEMENT(i))
1847 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1849 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1851 element_info[i].content.e[x][y] =
1852 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1853 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1854 i == EL_PLAYER_3 ? EL_EMERALD :
1855 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1856 i == EL_MOLE ? EL_EMERALD_RED :
1857 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1858 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1859 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1860 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1861 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1862 i == EL_WALL_EMERALD ? EL_EMERALD :
1863 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1864 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1865 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1866 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1867 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1868 i == EL_WALL_PEARL ? EL_PEARL :
1869 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1875 int get_num_special_action(int element, int action_first, int action_last)
1877 int num_special_action = 0;
1880 for (i = action_first; i <= action_last; i++)
1882 boolean found = FALSE;
1884 for (j = 0; j < NUM_DIRECTIONS; j++)
1885 if (el_act_dir2img(element, i, j) !=
1886 el_act_dir2img(element, ACTION_DEFAULT, j))
1890 num_special_action++;
1896 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1899 return num_special_action;
1903 =============================================================================
1905 -----------------------------------------------------------------------------
1906 initialize and start new game
1907 =============================================================================
1912 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1913 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1914 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1919 /* don't play tapes over network */
1920 network_playing = (options.network && !tape.playing);
1922 for (i = 0; i < MAX_PLAYERS; i++)
1924 struct PlayerInfo *player = &stored_player[i];
1926 player->index_nr = i;
1927 player->index_bit = (1 << i);
1928 player->element_nr = EL_PLAYER_1 + i;
1930 player->present = FALSE;
1931 player->active = FALSE;
1934 player->effective_action = 0;
1935 player->programmed_action = 0;
1938 player->gems_still_needed = level.gems_needed;
1939 player->sokobanfields_still_needed = 0;
1940 player->lights_still_needed = 0;
1941 player->friends_still_needed = 0;
1943 for (j = 0; j < MAX_NUM_KEYS; j++)
1944 player->key[j] = FALSE;
1946 player->dynabomb_count = 0;
1947 player->dynabomb_size = 1;
1948 player->dynabombs_left = 0;
1949 player->dynabomb_xl = FALSE;
1951 player->MovDir = MV_NONE;
1954 player->GfxDir = MV_NONE;
1955 player->GfxAction = ACTION_DEFAULT;
1957 player->StepFrame = 0;
1959 player->use_murphy = FALSE;
1960 player->artwork_element =
1961 (level.use_artwork_element[i] ? level.artwork_element[i] :
1962 player->element_nr);
1964 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1965 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1967 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1969 player->actual_frame_counter = 0;
1971 player->step_counter = 0;
1973 player->last_move_dir = MV_NONE;
1975 player->is_waiting = FALSE;
1976 player->is_moving = FALSE;
1977 player->is_auto_moving = FALSE;
1978 player->is_digging = FALSE;
1979 player->is_snapping = FALSE;
1980 player->is_collecting = FALSE;
1981 player->is_pushing = FALSE;
1982 player->is_switching = FALSE;
1983 player->is_dropping = FALSE;
1984 player->is_dropping_pressed = FALSE;
1986 player->is_bored = FALSE;
1987 player->is_sleeping = FALSE;
1989 player->frame_counter_bored = -1;
1990 player->frame_counter_sleeping = -1;
1992 player->anim_delay_counter = 0;
1993 player->post_delay_counter = 0;
1995 player->dir_waiting = MV_NONE;
1996 player->action_waiting = ACTION_DEFAULT;
1997 player->last_action_waiting = ACTION_DEFAULT;
1998 player->special_action_bored = ACTION_DEFAULT;
1999 player->special_action_sleeping = ACTION_DEFAULT;
2002 /* cannot be set here -- could be modified in Init[Player]Field() below */
2004 /* set number of special actions for bored and sleeping animation */
2005 player->num_special_action_bored =
2006 get_num_special_action(player->artwork_element,
2007 ACTION_BORING_1, ACTION_BORING_LAST);
2008 player->num_special_action_sleeping =
2009 get_num_special_action(player->artwork_element,
2010 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2013 player->switch_x = -1;
2014 player->switch_y = -1;
2016 player->drop_x = -1;
2017 player->drop_y = -1;
2019 player->show_envelope = 0;
2022 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
2024 player->move_delay = game.initial_move_delay;
2025 player->move_delay_value = game.initial_move_delay_value;
2027 player->move_delay_value_next = -1;
2029 player->move_delay_reset_counter = 0;
2031 player->cannot_move = FALSE;
2034 player->push_delay = -1; /* initialized when pushing starts */
2035 player->push_delay_value = game.initial_push_delay_value;
2037 player->drop_delay = 0;
2038 player->drop_pressed_delay = 0;
2040 player->last_jx = player->last_jy = 0;
2041 player->jx = player->jy = 0;
2043 player->shield_normal_time_left = 0;
2044 player->shield_deadly_time_left = 0;
2046 player->inventory_infinite_element = EL_UNDEFINED;
2047 player->inventory_size = 0;
2049 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2050 SnapField(player, 0, 0);
2052 player->LevelSolved = FALSE;
2053 player->GameOver = FALSE;
2056 network_player_action_received = FALSE;
2058 #if defined(NETWORK_AVALIABLE)
2059 /* initial null action */
2060 if (network_playing)
2061 SendToServer_MovePlayer(MV_NONE);
2070 TimeLeft = level.time;
2073 ScreenMovDir = MV_NONE;
2077 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2079 AllPlayersGone = FALSE;
2081 game.yamyam_content_nr = 0;
2082 game.magic_wall_active = FALSE;
2083 game.magic_wall_time_left = 0;
2084 game.light_time_left = 0;
2085 game.timegate_time_left = 0;
2086 game.switchgate_pos = 0;
2087 game.wind_direction = level.wind_direction_initial;
2088 game.gravity = level.initial_gravity;
2089 game.explosions_delayed = TRUE;
2091 game.lenses_time_left = 0;
2092 game.magnify_time_left = 0;
2094 game.ball_state = level.ball_state_initial;
2095 game.ball_content_nr = 0;
2097 game.envelope_active = FALSE;
2099 /* set focus to local player for network games, else to all players */
2100 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2101 game.centered_player_nr_next = game.centered_player_nr;
2102 game.set_centered_player = FALSE;
2104 if (network_playing && tape.recording)
2106 /* store client dependent player focus when recording network games */
2107 tape.centered_player_nr_next = game.centered_player_nr_next;
2108 tape.set_centered_player = TRUE;
2112 printf("::: focus set to player %d [%d]\n",
2113 game.centered_player_nr, local_player->index_nr);
2116 for (i = 0; i < NUM_BELTS; i++)
2118 game.belt_dir[i] = MV_NONE;
2119 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2122 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2123 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2126 SCAN_PLAYFIELD(x, y)
2128 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2131 Feld[x][y] = level.field[x][y];
2132 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2133 ChangeDelay[x][y] = 0;
2134 ChangePage[x][y] = -1;
2135 #if USE_NEW_CUSTOM_VALUE
2136 CustomValue[x][y] = 0; /* initialized in InitField() */
2138 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2140 WasJustMoving[x][y] = 0;
2141 WasJustFalling[x][y] = 0;
2142 CheckCollision[x][y] = 0;
2144 Pushed[x][y] = FALSE;
2146 ChangeCount[x][y] = 0;
2147 ChangeEvent[x][y] = -1;
2149 ExplodePhase[x][y] = 0;
2150 ExplodeDelay[x][y] = 0;
2151 ExplodeField[x][y] = EX_TYPE_NONE;
2153 RunnerVisit[x][y] = 0;
2154 PlayerVisit[x][y] = 0;
2157 GfxRandom[x][y] = INIT_GFX_RANDOM();
2158 GfxElement[x][y] = EL_UNDEFINED;
2159 GfxAction[x][y] = ACTION_DEFAULT;
2160 GfxDir[x][y] = MV_NONE;
2164 SCAN_PLAYFIELD(x, y)
2166 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2169 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2171 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2173 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2176 InitField(x, y, TRUE);
2181 for (i = 0; i < MAX_PLAYERS; i++)
2183 struct PlayerInfo *player = &stored_player[i];
2186 /* set number of special actions for bored and sleeping animation */
2187 player->num_special_action_bored =
2188 get_num_special_action(player->artwork_element,
2189 ACTION_BORING_1, ACTION_BORING_LAST);
2190 player->num_special_action_sleeping =
2191 get_num_special_action(player->artwork_element,
2192 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2197 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2198 emulate_sb ? EMU_SOKOBAN :
2199 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2201 #if USE_NEW_ALL_SLIPPERY
2202 /* initialize type of slippery elements */
2203 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2205 if (!IS_CUSTOM_ELEMENT(i))
2207 /* default: elements slip down either to the left or right randomly */
2208 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2210 /* SP style elements prefer to slip down on the left side */
2211 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2212 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2214 /* BD style elements prefer to slip down on the left side */
2215 if (game.emulation == EMU_BOULDERDASH)
2216 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2221 /* initialize explosion and ignition delay */
2222 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2224 if (!IS_CUSTOM_ELEMENT(i))
2227 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2228 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2229 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2230 int last_phase = (num_phase + 1) * delay;
2231 int half_phase = (num_phase / 2) * delay;
2233 element_info[i].explosion_delay = last_phase - 1;
2234 element_info[i].ignition_delay = half_phase;
2236 if (i == EL_BLACK_ORB)
2237 element_info[i].ignition_delay = 1;
2241 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2242 element_info[i].explosion_delay = 1;
2244 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2245 element_info[i].ignition_delay = 1;
2249 /* correct non-moving belts to start moving left */
2250 for (i = 0; i < NUM_BELTS; i++)
2251 if (game.belt_dir[i] == MV_NONE)
2252 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2254 /* check if any connected player was not found in playfield */
2255 for (i = 0; i < MAX_PLAYERS; i++)
2257 struct PlayerInfo *player = &stored_player[i];
2259 if (player->connected && !player->present)
2261 for (j = 0; j < MAX_PLAYERS; j++)
2263 struct PlayerInfo *some_player = &stored_player[j];
2264 int jx = some_player->jx, jy = some_player->jy;
2266 /* assign first free player found that is present in the playfield */
2267 if (some_player->present && !some_player->connected)
2269 player->present = TRUE;
2270 player->active = TRUE;
2272 some_player->present = FALSE;
2273 some_player->active = FALSE;
2276 player->element_nr = some_player->element_nr;
2279 player->artwork_element = some_player->artwork_element;
2281 player->block_last_field = some_player->block_last_field;
2282 player->block_delay_adjustment = some_player->block_delay_adjustment;
2284 StorePlayer[jx][jy] = player->element_nr;
2285 player->jx = player->last_jx = jx;
2286 player->jy = player->last_jy = jy;
2296 /* when playing a tape, eliminate all players who do not participate */
2298 for (i = 0; i < MAX_PLAYERS; i++)
2300 if (stored_player[i].active && !tape.player_participates[i])
2302 struct PlayerInfo *player = &stored_player[i];
2303 int jx = player->jx, jy = player->jy;
2305 player->active = FALSE;
2306 StorePlayer[jx][jy] = 0;
2307 Feld[jx][jy] = EL_EMPTY;
2311 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2313 /* when in single player mode, eliminate all but the first active player */
2315 for (i = 0; i < MAX_PLAYERS; i++)
2317 if (stored_player[i].active)
2319 for (j = i + 1; j < MAX_PLAYERS; j++)
2321 if (stored_player[j].active)
2323 struct PlayerInfo *player = &stored_player[j];
2324 int jx = player->jx, jy = player->jy;
2326 player->active = FALSE;
2327 player->present = FALSE;
2329 StorePlayer[jx][jy] = 0;
2330 Feld[jx][jy] = EL_EMPTY;
2337 /* when recording the game, store which players take part in the game */
2340 for (i = 0; i < MAX_PLAYERS; i++)
2341 if (stored_player[i].active)
2342 tape.player_participates[i] = TRUE;
2347 for (i = 0; i < MAX_PLAYERS; i++)
2349 struct PlayerInfo *player = &stored_player[i];
2351 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2356 if (local_player == player)
2357 printf("Player %d is local player.\n", i+1);
2361 if (BorderElement == EL_EMPTY)
2364 SBX_Right = lev_fieldx - SCR_FIELDX;
2366 SBY_Lower = lev_fieldy - SCR_FIELDY;
2371 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2373 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2376 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2377 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2379 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2380 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2382 /* if local player not found, look for custom element that might create
2383 the player (make some assumptions about the right custom element) */
2384 if (!local_player->present)
2386 int start_x = 0, start_y = 0;
2387 int found_rating = 0;
2388 int found_element = EL_UNDEFINED;
2389 int player_nr = local_player->index_nr;
2392 SCAN_PLAYFIELD(x, y)
2394 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2397 int element = Feld[x][y];
2402 if (level.use_start_element[player_nr] &&
2403 level.start_element[player_nr] == element &&
2410 found_element = element;
2413 if (!IS_CUSTOM_ELEMENT(element))
2416 if (CAN_CHANGE(element))
2418 for (i = 0; i < element_info[element].num_change_pages; i++)
2420 /* check for player created from custom element as single target */
2421 content = element_info[element].change_page[i].target_element;
2422 is_player = ELEM_IS_PLAYER(content);
2424 if (is_player && (found_rating < 3 || element < found_element))
2430 found_element = element;
2435 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2437 /* check for player created from custom element as explosion content */
2438 content = element_info[element].content.e[xx][yy];
2439 is_player = ELEM_IS_PLAYER(content);
2441 if (is_player && (found_rating < 2 || element < found_element))
2443 start_x = x + xx - 1;
2444 start_y = y + yy - 1;
2447 found_element = element;
2450 if (!CAN_CHANGE(element))
2453 for (i = 0; i < element_info[element].num_change_pages; i++)
2455 /* check for player created from custom element as extended target */
2457 element_info[element].change_page[i].target_content.e[xx][yy];
2459 is_player = ELEM_IS_PLAYER(content);
2461 if (is_player && (found_rating < 1 || element < found_element))
2463 start_x = x + xx - 1;
2464 start_y = y + yy - 1;
2467 found_element = element;
2473 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2474 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2477 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2478 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2483 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2484 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2485 local_player->jx - MIDPOSX);
2487 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2488 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2489 local_player->jy - MIDPOSY);
2492 if (!game.restart_level)
2493 CloseDoor(DOOR_CLOSE_1);
2495 /* !!! FIX THIS (START) !!! */
2496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2498 InitGameEngine_EM();
2505 /* after drawing the level, correct some elements */
2506 if (game.timegate_time_left == 0)
2507 CloseAllOpenTimegates();
2509 if (setup.soft_scrolling)
2510 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2512 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2515 /* !!! FIX THIS (END) !!! */
2517 if (!game.restart_level)
2519 /* copy default game door content to main double buffer */
2520 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2521 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2524 DrawGameDoorValues();
2526 if (!game.restart_level)
2530 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2531 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2532 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2536 /* copy actual game door content to door double buffer for OpenDoor() */
2537 BlitBitmap(drawto, bitmap_db_door,
2538 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2540 OpenDoor(DOOR_OPEN_ALL);
2542 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2544 if (setup.sound_music)
2547 KeyboardAutoRepeatOffUnlessAutoplay();
2551 for (i = 0; i < MAX_PLAYERS; i++)
2552 printf("Player %d %sactive.\n",
2553 i + 1, (stored_player[i].active ? "" : "not "));
2557 game.restart_level = FALSE;
2560 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2562 /* this is used for non-R'n'D game engines to update certain engine values */
2564 /* needed to determine if sounds are played within the visible screen area */
2565 scroll_x = actual_scroll_x;
2566 scroll_y = actual_scroll_y;
2569 void InitMovDir(int x, int y)
2571 int i, element = Feld[x][y];
2572 static int xy[4][2] =
2579 static int direction[3][4] =
2581 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2582 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2583 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2592 Feld[x][y] = EL_BUG;
2593 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2596 case EL_SPACESHIP_RIGHT:
2597 case EL_SPACESHIP_UP:
2598 case EL_SPACESHIP_LEFT:
2599 case EL_SPACESHIP_DOWN:
2600 Feld[x][y] = EL_SPACESHIP;
2601 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2604 case EL_BD_BUTTERFLY_RIGHT:
2605 case EL_BD_BUTTERFLY_UP:
2606 case EL_BD_BUTTERFLY_LEFT:
2607 case EL_BD_BUTTERFLY_DOWN:
2608 Feld[x][y] = EL_BD_BUTTERFLY;
2609 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2612 case EL_BD_FIREFLY_RIGHT:
2613 case EL_BD_FIREFLY_UP:
2614 case EL_BD_FIREFLY_LEFT:
2615 case EL_BD_FIREFLY_DOWN:
2616 Feld[x][y] = EL_BD_FIREFLY;
2617 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2620 case EL_PACMAN_RIGHT:
2622 case EL_PACMAN_LEFT:
2623 case EL_PACMAN_DOWN:
2624 Feld[x][y] = EL_PACMAN;
2625 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2628 case EL_YAMYAM_LEFT:
2629 case EL_YAMYAM_RIGHT:
2631 case EL_YAMYAM_DOWN:
2632 Feld[x][y] = EL_YAMYAM;
2633 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2636 case EL_SP_SNIKSNAK:
2637 MovDir[x][y] = MV_UP;
2640 case EL_SP_ELECTRON:
2641 MovDir[x][y] = MV_LEFT;
2648 Feld[x][y] = EL_MOLE;
2649 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2653 if (IS_CUSTOM_ELEMENT(element))
2655 struct ElementInfo *ei = &element_info[element];
2656 int move_direction_initial = ei->move_direction_initial;
2657 int move_pattern = ei->move_pattern;
2659 if (move_direction_initial == MV_START_PREVIOUS)
2661 if (MovDir[x][y] != MV_NONE)
2664 move_direction_initial = MV_START_AUTOMATIC;
2667 if (move_direction_initial == MV_START_RANDOM)
2668 MovDir[x][y] = 1 << RND(4);
2669 else if (move_direction_initial & MV_ANY_DIRECTION)
2670 MovDir[x][y] = move_direction_initial;
2671 else if (move_pattern == MV_ALL_DIRECTIONS ||
2672 move_pattern == MV_TURNING_LEFT ||
2673 move_pattern == MV_TURNING_RIGHT ||
2674 move_pattern == MV_TURNING_LEFT_RIGHT ||
2675 move_pattern == MV_TURNING_RIGHT_LEFT ||
2676 move_pattern == MV_TURNING_RANDOM)
2677 MovDir[x][y] = 1 << RND(4);
2678 else if (move_pattern == MV_HORIZONTAL)
2679 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2680 else if (move_pattern == MV_VERTICAL)
2681 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2682 else if (move_pattern & MV_ANY_DIRECTION)
2683 MovDir[x][y] = element_info[element].move_pattern;
2684 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2685 move_pattern == MV_ALONG_RIGHT_SIDE)
2687 /* use random direction as default start direction */
2688 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2689 MovDir[x][y] = 1 << RND(4);
2691 for (i = 0; i < NUM_DIRECTIONS; i++)
2693 int x1 = x + xy[i][0];
2694 int y1 = y + xy[i][1];
2696 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2698 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2699 MovDir[x][y] = direction[0][i];
2701 MovDir[x][y] = direction[1][i];
2710 MovDir[x][y] = 1 << RND(4);
2712 if (element != EL_BUG &&
2713 element != EL_SPACESHIP &&
2714 element != EL_BD_BUTTERFLY &&
2715 element != EL_BD_FIREFLY)
2718 for (i = 0; i < NUM_DIRECTIONS; i++)
2720 int x1 = x + xy[i][0];
2721 int y1 = y + xy[i][1];
2723 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2725 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2727 MovDir[x][y] = direction[0][i];
2730 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2731 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2733 MovDir[x][y] = direction[1][i];
2742 GfxDir[x][y] = MovDir[x][y];
2745 void InitAmoebaNr(int x, int y)
2748 int group_nr = AmoebeNachbarNr(x, y);
2752 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2754 if (AmoebaCnt[i] == 0)
2762 AmoebaNr[x][y] = group_nr;
2763 AmoebaCnt[group_nr]++;
2764 AmoebaCnt2[group_nr]++;
2770 boolean raise_level = FALSE;
2772 if (local_player->MovPos)
2775 if (tape.auto_play) /* tape might already be stopped here */
2776 tape.auto_play_level_solved = TRUE;
2778 local_player->LevelSolved = FALSE;
2780 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2784 if (!tape.playing && setup.sound_loops)
2785 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2786 SND_CTRL_PLAY_LOOP);
2788 while (TimeLeft > 0)
2790 if (!tape.playing && !setup.sound_loops)
2791 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2793 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2796 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2801 RaiseScore(level.score[SC_TIME_BONUS]);
2804 DrawGameValue_Time(TimeLeft);
2812 if (!tape.playing && setup.sound_loops)
2813 StopSound(SND_GAME_LEVELTIME_BONUS);
2815 else if (level.time == 0) /* level without time limit */
2817 if (!tape.playing && setup.sound_loops)
2818 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2819 SND_CTRL_PLAY_LOOP);
2821 while (TimePlayed < 999)
2823 if (!tape.playing && !setup.sound_loops)
2824 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2826 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2829 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2834 RaiseScore(level.score[SC_TIME_BONUS]);
2837 DrawGameValue_Time(TimePlayed);
2845 if (!tape.playing && setup.sound_loops)
2846 StopSound(SND_GAME_LEVELTIME_BONUS);
2849 /* close exit door after last player */
2850 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2851 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2852 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2854 int element = Feld[ExitX][ExitY];
2856 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2857 EL_SP_EXIT_CLOSING);
2859 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2862 /* player disappears */
2863 if (ExitX >= 0 && ExitY >= 0)
2864 DrawLevelField(ExitX, ExitY);
2870 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2872 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2878 CloseDoor(DOOR_CLOSE_1);
2883 SaveTape(tape.level_nr); /* Ask to save tape */
2886 if (level_nr == leveldir_current->handicap_level)
2888 leveldir_current->handicap_level++;
2889 SaveLevelSetup_SeriesInfo();
2892 if (level_editor_test_game)
2893 local_player->score = -1; /* no highscore when playing from editor */
2894 else if (level_nr < leveldir_current->last_level)
2895 raise_level = TRUE; /* advance to next level */
2897 if ((hi_pos = NewHiScore()) >= 0)
2899 game_status = GAME_MODE_SCORES;
2900 DrawHallOfFame(hi_pos);
2909 game_status = GAME_MODE_MAIN;
2926 LoadScore(level_nr);
2928 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2929 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2932 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2934 if (local_player->score > highscore[k].Score)
2936 /* player has made it to the hall of fame */
2938 if (k < MAX_SCORE_ENTRIES - 1)
2940 int m = MAX_SCORE_ENTRIES - 1;
2943 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2944 if (strEqual(setup.player_name, highscore[l].Name))
2946 if (m == k) /* player's new highscore overwrites his old one */
2950 for (l = m; l > k; l--)
2952 strcpy(highscore[l].Name, highscore[l - 1].Name);
2953 highscore[l].Score = highscore[l - 1].Score;
2960 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2961 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2962 highscore[k].Score = local_player->score;
2968 else if (!strncmp(setup.player_name, highscore[k].Name,
2969 MAX_PLAYER_NAME_LEN))
2970 break; /* player already there with a higher score */
2976 SaveScore(level_nr);
2981 inline static int getElementMoveStepsize(int x, int y)
2983 int element = Feld[x][y];
2984 int direction = MovDir[x][y];
2985 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2986 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2987 int horiz_move = (dx != 0);
2988 int sign = (horiz_move ? dx : dy);
2989 int step = sign * element_info[element].move_stepsize;
2991 /* special values for move stepsize for spring and things on conveyor belt */
2995 if (element == EL_SPRING)
2996 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2997 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2998 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2999 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3001 if (CAN_FALL(element) &&
3002 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3003 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3004 else if (element == EL_SPRING)
3005 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3012 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3014 if (player->GfxAction != action || player->GfxDir != dir)
3017 printf("Player frame reset! (%d => %d, %d => %d)\n",
3018 player->GfxAction, action, player->GfxDir, dir);
3021 player->GfxAction = action;
3022 player->GfxDir = dir;
3024 player->StepFrame = 0;
3028 #if USE_GFX_RESET_GFX_ANIMATION
3029 static void ResetGfxFrame(int x, int y, boolean redraw)
3031 int element = Feld[x][y];
3032 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3033 int last_gfx_frame = GfxFrame[x][y];
3035 if (graphic_info[graphic].anim_global_sync)
3036 GfxFrame[x][y] = FrameCounter;
3037 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3038 GfxFrame[x][y] = CustomValue[x][y];
3039 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3040 GfxFrame[x][y] = element_info[element].collect_score;
3042 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3043 DrawLevelGraphicAnimation(x, y, graphic);
3047 static void ResetGfxAnimation(int x, int y)
3050 int element, graphic;
3053 GfxAction[x][y] = ACTION_DEFAULT;
3054 GfxDir[x][y] = MovDir[x][y];
3058 element = Feld[x][y];
3059 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3061 if (graphic_info[graphic].anim_global_sync)
3062 GfxFrame[x][y] = FrameCounter;
3063 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3064 GfxFrame[x][y] = CustomValue[x][y];
3065 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3066 GfxFrame[x][y] = element_info[element].collect_score;
3069 #if USE_GFX_RESET_GFX_ANIMATION
3070 ResetGfxFrame(x, y, FALSE);
3074 static void ResetRandomAnimationValue(int x, int y)
3076 GfxRandom[x][y] = INIT_GFX_RANDOM();
3079 void InitMovingField(int x, int y, int direction)
3081 int element = Feld[x][y];
3085 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3086 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3090 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3091 ResetGfxAnimation(x, y);
3093 MovDir[x][y] = direction;
3094 GfxDir[x][y] = direction;
3095 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3096 ACTION_FALLING : ACTION_MOVING);
3099 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3101 if (graphic_info[graphic].anim_global_sync)
3102 GfxFrame[x][y] = FrameCounter;
3103 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3104 GfxFrame[x][y] = CustomValue[x][y];
3105 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3106 GfxFrame[x][y] = element_info[element].collect_score;
3109 /* this is needed for CEs with property "can move" / "not moving" */
3111 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3113 if (Feld[newx][newy] == EL_EMPTY)
3114 Feld[newx][newy] = EL_BLOCKED;
3116 MovDir[newx][newy] = MovDir[x][y];
3118 #if USE_NEW_CUSTOM_VALUE
3119 CustomValue[newx][newy] = CustomValue[x][y];
3122 GfxFrame[newx][newy] = GfxFrame[x][y];
3123 GfxRandom[newx][newy] = GfxRandom[x][y];
3124 GfxAction[newx][newy] = GfxAction[x][y];
3125 GfxDir[newx][newy] = GfxDir[x][y];
3129 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3131 int direction = MovDir[x][y];
3133 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3134 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3136 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3137 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3144 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3146 int oldx = x, oldy = y;
3147 int direction = MovDir[x][y];
3149 if (direction == MV_LEFT)
3151 else if (direction == MV_RIGHT)
3153 else if (direction == MV_UP)
3155 else if (direction == MV_DOWN)
3158 *comes_from_x = oldx;
3159 *comes_from_y = oldy;
3162 int MovingOrBlocked2Element(int x, int y)
3164 int element = Feld[x][y];
3166 if (element == EL_BLOCKED)
3170 Blocked2Moving(x, y, &oldx, &oldy);
3171 return Feld[oldx][oldy];
3177 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3179 /* like MovingOrBlocked2Element(), but if element is moving
3180 and (x,y) is the field the moving element is just leaving,
3181 return EL_BLOCKED instead of the element value */
3182 int element = Feld[x][y];
3184 if (IS_MOVING(x, y))
3186 if (element == EL_BLOCKED)
3190 Blocked2Moving(x, y, &oldx, &oldy);
3191 return Feld[oldx][oldy];
3200 static void RemoveField(int x, int y)
3202 Feld[x][y] = EL_EMPTY;
3208 #if USE_NEW_CUSTOM_VALUE
3209 CustomValue[x][y] = 0;
3213 ChangeDelay[x][y] = 0;
3214 ChangePage[x][y] = -1;
3215 Pushed[x][y] = FALSE;
3218 ExplodeField[x][y] = EX_TYPE_NONE;
3221 GfxElement[x][y] = EL_UNDEFINED;
3222 GfxAction[x][y] = ACTION_DEFAULT;
3223 GfxDir[x][y] = MV_NONE;
3226 void RemoveMovingField(int x, int y)
3228 int oldx = x, oldy = y, newx = x, newy = y;
3229 int element = Feld[x][y];
3230 int next_element = EL_UNDEFINED;
3232 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3235 if (IS_MOVING(x, y))
3237 Moving2Blocked(x, y, &newx, &newy);
3239 if (Feld[newx][newy] != EL_BLOCKED)
3241 /* element is moving, but target field is not free (blocked), but
3242 already occupied by something different (example: acid pool);
3243 in this case, only remove the moving field, but not the target */
3245 RemoveField(oldx, oldy);
3247 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3249 DrawLevelField(oldx, oldy);
3254 else if (element == EL_BLOCKED)
3256 Blocked2Moving(x, y, &oldx, &oldy);
3257 if (!IS_MOVING(oldx, oldy))
3261 if (element == EL_BLOCKED &&
3262 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3263 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3264 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3265 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3266 next_element = get_next_element(Feld[oldx][oldy]);
3268 RemoveField(oldx, oldy);
3269 RemoveField(newx, newy);
3271 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3273 if (next_element != EL_UNDEFINED)
3274 Feld[oldx][oldy] = next_element;
3276 DrawLevelField(oldx, oldy);
3277 DrawLevelField(newx, newy);
3280 void DrawDynamite(int x, int y)
3282 int sx = SCREENX(x), sy = SCREENY(y);
3283 int graphic = el2img(Feld[x][y]);
3286 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3289 if (IS_WALKABLE_INSIDE(Back[x][y]))
3293 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3294 else if (Store[x][y])
3295 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3297 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3299 if (Back[x][y] || Store[x][y])
3300 DrawGraphicThruMask(sx, sy, graphic, frame);
3302 DrawGraphic(sx, sy, graphic, frame);
3305 void CheckDynamite(int x, int y)
3307 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3311 if (MovDelay[x][y] != 0)
3314 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3320 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3327 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3329 boolean num_checked_players = 0;
3332 for (i = 0; i < MAX_PLAYERS; i++)
3334 if (stored_player[i].active)
3336 int sx = stored_player[i].jx;
3337 int sy = stored_player[i].jy;
3339 if (num_checked_players == 0)
3346 *sx1 = MIN(*sx1, sx);
3347 *sy1 = MIN(*sy1, sy);
3348 *sx2 = MAX(*sx2, sx);
3349 *sy2 = MAX(*sy2, sy);
3352 num_checked_players++;
3357 static boolean checkIfAllPlayersFitToScreen_RND()
3359 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3361 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3363 return (sx2 - sx1 < SCR_FIELDX &&
3364 sy2 - sy1 < SCR_FIELDY);
3367 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3369 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3371 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3373 *sx = (sx1 + sx2) / 2;
3374 *sy = (sy1 + sy2) / 2;
3378 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3379 int center_x, int center_y)
3381 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3383 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3385 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3386 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3389 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3393 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3395 return (max_dx <= SCR_FIELDX / 2 &&
3396 max_dy <= SCR_FIELDY / 2);
3404 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3405 boolean quick_relocation)
3407 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3408 boolean no_delay = (tape.warp_forward);
3409 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3410 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3412 if (quick_relocation)
3414 int offset = (setup.scroll_delay ? 3 : 0);
3421 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3423 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3424 x > SBX_Right + MIDPOSX ? SBX_Right :
3427 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3428 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3433 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3434 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3435 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3437 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3438 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3439 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3441 /* don't scroll over playfield boundaries */
3442 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3443 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3445 /* don't scroll over playfield boundaries */
3446 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3447 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3450 RedrawPlayfield(TRUE, 0,0,0,0);
3454 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3455 x > SBX_Right + MIDPOSX ? SBX_Right :
3458 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3459 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3462 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3464 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3467 int fx = FX, fy = FY;
3469 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3470 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3472 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3478 fx += dx * TILEX / 2;
3479 fy += dy * TILEY / 2;
3481 ScrollLevel(dx, dy);
3484 /* scroll in two steps of half tile size to make things smoother */
3485 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3487 Delay(wait_delay_value);
3489 /* scroll second step to align at full tile size */
3491 Delay(wait_delay_value);
3496 Delay(wait_delay_value);
3502 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3504 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3505 boolean no_delay = (tape.warp_forward);
3506 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3507 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3508 int jx = player->jx;
3509 int jy = player->jy;
3511 if (quick_relocation)
3513 int offset = (setup.scroll_delay ? 3 : 0);
3515 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3517 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3518 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3519 player->jx - MIDPOSX);
3521 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3522 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3523 player->jy - MIDPOSY);
3527 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3528 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3529 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3531 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3532 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3533 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3535 /* don't scroll over playfield boundaries */
3536 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3537 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3539 /* don't scroll over playfield boundaries */
3540 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3541 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3544 RedrawPlayfield(TRUE, 0,0,0,0);
3548 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3549 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3550 player->jx - MIDPOSX);
3552 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3553 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3554 player->jy - MIDPOSY);
3556 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3558 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3561 int fx = FX, fy = FY;
3563 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3564 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3566 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3572 fx += dx * TILEX / 2;
3573 fy += dy * TILEY / 2;
3575 ScrollLevel(dx, dy);
3578 /* scroll in two steps of half tile size to make things smoother */
3579 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3581 Delay(wait_delay_value);
3583 /* scroll second step to align at full tile size */
3585 Delay(wait_delay_value);
3590 Delay(wait_delay_value);
3596 void RelocatePlayer(int jx, int jy, int el_player_raw)
3598 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3599 int player_nr = GET_PLAYER_NR(el_player);
3600 struct PlayerInfo *player = &stored_player[player_nr];
3601 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3602 boolean no_delay = (tape.warp_forward);
3603 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3604 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3605 int old_jx = player->jx;
3606 int old_jy = player->jy;
3607 int old_element = Feld[old_jx][old_jy];
3608 int element = Feld[jx][jy];
3609 boolean player_relocated = (old_jx != jx || old_jy != jy);
3611 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3612 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3613 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3614 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3615 int leave_side_horiz = move_dir_horiz;
3616 int leave_side_vert = move_dir_vert;
3617 int enter_side = enter_side_horiz | enter_side_vert;
3618 int leave_side = leave_side_horiz | leave_side_vert;
3620 if (player->GameOver) /* do not reanimate dead player */
3623 if (!player_relocated) /* no need to relocate the player */
3626 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3628 RemoveField(jx, jy); /* temporarily remove newly placed player */
3629 DrawLevelField(jx, jy);
3632 if (player->present)
3634 while (player->MovPos)
3636 ScrollPlayer(player, SCROLL_GO_ON);
3637 ScrollScreen(NULL, SCROLL_GO_ON);
3639 AdvanceFrameAndPlayerCounters(player->index_nr);
3644 Delay(wait_delay_value);
3647 DrawPlayer(player); /* needed here only to cleanup last field */
3648 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3650 player->is_moving = FALSE;
3653 if (IS_CUSTOM_ELEMENT(old_element))
3654 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3656 player->index_bit, leave_side);
3658 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3660 player->index_bit, leave_side);
3662 Feld[jx][jy] = el_player;
3663 InitPlayerField(jx, jy, el_player, TRUE);
3665 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3667 Feld[jx][jy] = element;
3668 InitField(jx, jy, FALSE);
3672 /* only visually relocate centered player */
3674 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3675 level.instant_relocation);
3677 if (player->index_nr == game.centered_player_nr)
3678 DrawRelocatePlayer(player, level.instant_relocation);
3681 if (player == local_player) /* only visually relocate local player */
3682 DrawRelocatePlayer(player, level.instant_relocation);
3685 TestIfPlayerTouchesBadThing(jx, jy);
3686 TestIfPlayerTouchesCustomElement(jx, jy);
3688 if (IS_CUSTOM_ELEMENT(element))
3689 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3690 player->index_bit, enter_side);
3692 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3693 player->index_bit, enter_side);
3696 void Explode(int ex, int ey, int phase, int mode)
3702 /* !!! eliminate this variable !!! */
3703 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3705 if (game.explosions_delayed)
3707 ExplodeField[ex][ey] = mode;
3711 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3713 int center_element = Feld[ex][ey];
3714 int artwork_element, explosion_element; /* set these values later */
3717 /* --- This is only really needed (and now handled) in "Impact()". --- */
3718 /* do not explode moving elements that left the explode field in time */
3719 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3720 center_element == EL_EMPTY &&
3721 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3726 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3727 if (mode == EX_TYPE_NORMAL ||
3728 mode == EX_TYPE_CENTER ||
3729 mode == EX_TYPE_CROSS)
3730 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3733 /* remove things displayed in background while burning dynamite */
3734 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3737 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3739 /* put moving element to center field (and let it explode there) */
3740 center_element = MovingOrBlocked2Element(ex, ey);
3741 RemoveMovingField(ex, ey);
3742 Feld[ex][ey] = center_element;
3745 /* now "center_element" is finally determined -- set related values now */
3746 artwork_element = center_element; /* for custom player artwork */
3747 explosion_element = center_element; /* for custom player artwork */
3749 if (IS_PLAYER(ex, ey))
3751 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3753 artwork_element = stored_player[player_nr].artwork_element;
3755 if (level.use_explosion_element[player_nr])
3757 explosion_element = level.explosion_element[player_nr];
3758 artwork_element = explosion_element;
3763 if (mode == EX_TYPE_NORMAL ||
3764 mode == EX_TYPE_CENTER ||
3765 mode == EX_TYPE_CROSS)
3766 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3770 last_phase = element_info[explosion_element].explosion_delay + 1;
3772 last_phase = element_info[center_element].explosion_delay + 1;
3775 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3777 int xx = x - ex + 1;
3778 int yy = y - ey + 1;
3781 if (!IN_LEV_FIELD(x, y) ||
3782 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3783 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3786 element = Feld[x][y];
3788 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3790 element = MovingOrBlocked2Element(x, y);
3792 if (!IS_EXPLOSION_PROOF(element))
3793 RemoveMovingField(x, y);
3796 /* indestructible elements can only explode in center (but not flames) */
3797 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3798 mode == EX_TYPE_BORDER)) ||
3799 element == EL_FLAMES)
3802 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3803 behaviour, for example when touching a yamyam that explodes to rocks
3804 with active deadly shield, a rock is created under the player !!! */
3805 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3807 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3808 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3809 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3811 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3814 if (IS_ACTIVE_BOMB(element))
3816 /* re-activate things under the bomb like gate or penguin */
3817 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3824 /* save walkable background elements while explosion on same tile */
3825 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3826 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3827 Back[x][y] = element;
3829 /* ignite explodable elements reached by other explosion */
3830 if (element == EL_EXPLOSION)
3831 element = Store2[x][y];
3833 if (AmoebaNr[x][y] &&
3834 (element == EL_AMOEBA_FULL ||
3835 element == EL_BD_AMOEBA ||
3836 element == EL_AMOEBA_GROWING))
3838 AmoebaCnt[AmoebaNr[x][y]]--;
3839 AmoebaCnt2[AmoebaNr[x][y]]--;
3844 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3847 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3849 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3851 switch(StorePlayer[ex][ey])
3854 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3857 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3860 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3864 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3869 if (PLAYERINFO(ex, ey)->use_murphy)
3870 Store[x][y] = EL_EMPTY;
3873 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3874 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3875 else if (ELEM_IS_PLAYER(center_element))
3876 Store[x][y] = EL_EMPTY;
3877 else if (center_element == EL_YAMYAM)
3878 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3879 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3880 Store[x][y] = element_info[center_element].content.e[xx][yy];
3882 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3883 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3884 otherwise) -- FIX THIS !!! */
3885 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3886 Store[x][y] = element_info[element].content.e[1][1];
3888 else if (!CAN_EXPLODE(element))
3889 Store[x][y] = element_info[element].content.e[1][1];
3892 Store[x][y] = EL_EMPTY;
3894 else if (center_element == EL_MOLE)
3895 Store[x][y] = EL_EMERALD_RED;
3896 else if (center_element == EL_PENGUIN)
3897 Store[x][y] = EL_EMERALD_PURPLE;
3898 else if (center_element == EL_BUG)
3899 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3900 else if (center_element == EL_BD_BUTTERFLY)
3901 Store[x][y] = EL_BD_DIAMOND;
3902 else if (center_element == EL_SP_ELECTRON)
3903 Store[x][y] = EL_SP_INFOTRON;
3904 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3905 Store[x][y] = level.amoeba_content;
3906 else if (center_element == EL_YAMYAM)
3907 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3908 else if (IS_CUSTOM_ELEMENT(center_element) &&
3909 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3910 Store[x][y] = element_info[center_element].content.e[xx][yy];
3911 else if (element == EL_WALL_EMERALD)
3912 Store[x][y] = EL_EMERALD;
3913 else if (element == EL_WALL_DIAMOND)
3914 Store[x][y] = EL_DIAMOND;
3915 else if (element == EL_WALL_BD_DIAMOND)
3916 Store[x][y] = EL_BD_DIAMOND;
3917 else if (element == EL_WALL_EMERALD_YELLOW)
3918 Store[x][y] = EL_EMERALD_YELLOW;
3919 else if (element == EL_WALL_EMERALD_RED)
3920 Store[x][y] = EL_EMERALD_RED;
3921 else if (element == EL_WALL_EMERALD_PURPLE)
3922 Store[x][y] = EL_EMERALD_PURPLE;
3923 else if (element == EL_WALL_PEARL)
3924 Store[x][y] = EL_PEARL;
3925 else if (element == EL_WALL_CRYSTAL)
3926 Store[x][y] = EL_CRYSTAL;
3927 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3928 Store[x][y] = element_info[element].content.e[1][1];
3930 Store[x][y] = EL_EMPTY;
3933 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3934 center_element == EL_AMOEBA_TO_DIAMOND)
3935 Store2[x][y] = element;
3937 Feld[x][y] = EL_EXPLOSION;
3938 GfxElement[x][y] = artwork_element;
3941 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3942 x, y, artwork_element, EL_NAME(artwork_element));
3945 ExplodePhase[x][y] = 1;
3946 ExplodeDelay[x][y] = last_phase;
3951 if (center_element == EL_YAMYAM)
3952 game.yamyam_content_nr =
3953 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3965 GfxFrame[x][y] = 0; /* restart explosion animation */
3967 last_phase = ExplodeDelay[x][y];
3969 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3973 /* activate this even in non-DEBUG version until cause for crash in
3974 getGraphicAnimationFrame() (see below) is found and eliminated */
3980 /* this can happen if the player leaves an explosion just in time */
3981 if (GfxElement[x][y] == EL_UNDEFINED)
3982 GfxElement[x][y] = EL_EMPTY;
3984 if (GfxElement[x][y] == EL_UNDEFINED)
3987 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3988 printf("Explode(): This should never happen!\n");
3991 GfxElement[x][y] = EL_EMPTY;
3997 border_element = Store2[x][y];
3998 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3999 border_element = StorePlayer[x][y];
4001 if (phase == element_info[border_element].ignition_delay ||
4002 phase == last_phase)
4004 boolean border_explosion = FALSE;
4006 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4007 !PLAYER_EXPLOSION_PROTECTED(x, y))
4009 KillPlayerUnlessExplosionProtected(x, y);
4010 border_explosion = TRUE;
4012 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4014 Feld[x][y] = Store2[x][y];
4017 border_explosion = TRUE;
4019 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4021 AmoebeUmwandeln(x, y);
4023 border_explosion = TRUE;
4026 /* if an element just explodes due to another explosion (chain-reaction),
4027 do not immediately end the new explosion when it was the last frame of
4028 the explosion (as it would be done in the following "if"-statement!) */
4029 if (border_explosion && phase == last_phase)
4033 if (phase == last_phase)
4037 element = Feld[x][y] = Store[x][y];
4038 Store[x][y] = Store2[x][y] = 0;
4039 GfxElement[x][y] = EL_UNDEFINED;
4041 /* player can escape from explosions and might therefore be still alive */
4042 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4043 element <= EL_PLAYER_IS_EXPLODING_4)
4045 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4046 int explosion_element = EL_PLAYER_1 + player_nr;
4047 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4048 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4050 if (level.use_explosion_element[player_nr])
4051 explosion_element = level.explosion_element[player_nr];
4053 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4054 element_info[explosion_element].content.e[xx][yy]);
4057 /* restore probably existing indestructible background element */
4058 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4059 element = Feld[x][y] = Back[x][y];
4062 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4063 GfxDir[x][y] = MV_NONE;
4064 ChangeDelay[x][y] = 0;
4065 ChangePage[x][y] = -1;
4067 #if USE_NEW_CUSTOM_VALUE
4068 CustomValue[x][y] = 0;
4071 InitField_WithBug2(x, y, FALSE);
4073 DrawLevelField(x, y);
4075 TestIfElementTouchesCustomElement(x, y);
4077 if (GFX_CRUMBLED(element))
4078 DrawLevelFieldCrumbledSandNeighbours(x, y);
4080 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4081 StorePlayer[x][y] = 0;
4083 if (ELEM_IS_PLAYER(element))
4084 RelocatePlayer(x, y, element);
4086 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4088 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4089 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4092 DrawLevelFieldCrumbledSand(x, y);
4094 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4096 DrawLevelElement(x, y, Back[x][y]);
4097 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4099 else if (IS_WALKABLE_UNDER(Back[x][y]))
4101 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4102 DrawLevelElementThruMask(x, y, Back[x][y]);
4104 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4105 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4109 void DynaExplode(int ex, int ey)
4112 int dynabomb_element = Feld[ex][ey];
4113 int dynabomb_size = 1;
4114 boolean dynabomb_xl = FALSE;
4115 struct PlayerInfo *player;
4116 static int xy[4][2] =
4124 if (IS_ACTIVE_BOMB(dynabomb_element))
4126 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4127 dynabomb_size = player->dynabomb_size;
4128 dynabomb_xl = player->dynabomb_xl;
4129 player->dynabombs_left++;
4132 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4134 for (i = 0; i < NUM_DIRECTIONS; i++)
4136 for (j = 1; j <= dynabomb_size; j++)
4138 int x = ex + j * xy[i][0];
4139 int y = ey + j * xy[i][1];
4142 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4145 element = Feld[x][y];
4147 /* do not restart explosions of fields with active bombs */
4148 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4151 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4153 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4154 !IS_DIGGABLE(element) && !dynabomb_xl)
4160 void Bang(int x, int y)
4162 int element = MovingOrBlocked2Element(x, y);
4163 int explosion_type = EX_TYPE_NORMAL;
4165 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4167 struct PlayerInfo *player = PLAYERINFO(x, y);
4169 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4170 player->element_nr);
4172 if (level.use_explosion_element[player->index_nr])
4174 int explosion_element = level.explosion_element[player->index_nr];
4176 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4177 explosion_type = EX_TYPE_CROSS;
4178 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4179 explosion_type = EX_TYPE_CENTER;
4187 case EL_BD_BUTTERFLY:
4190 case EL_DARK_YAMYAM:
4194 RaiseScoreElement(element);
4197 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4198 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4199 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4200 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4201 case EL_DYNABOMB_INCREASE_NUMBER:
4202 case EL_DYNABOMB_INCREASE_SIZE:
4203 case EL_DYNABOMB_INCREASE_POWER:
4204 explosion_type = EX_TYPE_DYNA;
4209 case EL_LAMP_ACTIVE:
4210 case EL_AMOEBA_TO_DIAMOND:
4211 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4212 explosion_type = EX_TYPE_CENTER;
4216 if (element_info[element].explosion_type == EXPLODES_CROSS)
4217 explosion_type = EX_TYPE_CROSS;
4218 else if (element_info[element].explosion_type == EXPLODES_1X1)
4219 explosion_type = EX_TYPE_CENTER;
4223 if (explosion_type == EX_TYPE_DYNA)
4226 Explode(x, y, EX_PHASE_START, explosion_type);
4228 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4231 void SplashAcid(int x, int y)
4233 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4234 (!IN_LEV_FIELD(x - 1, y - 2) ||
4235 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4236 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4238 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4239 (!IN_LEV_FIELD(x + 1, y - 2) ||
4240 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4241 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4243 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4246 static void InitBeltMovement()
4248 static int belt_base_element[4] =
4250 EL_CONVEYOR_BELT_1_LEFT,
4251 EL_CONVEYOR_BELT_2_LEFT,
4252 EL_CONVEYOR_BELT_3_LEFT,
4253 EL_CONVEYOR_BELT_4_LEFT
4255 static int belt_base_active_element[4] =
4257 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4258 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4259 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4260 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4265 /* set frame order for belt animation graphic according to belt direction */
4266 for (i = 0; i < NUM_BELTS; i++)
4270 for (j = 0; j < NUM_BELT_PARTS; j++)
4272 int element = belt_base_active_element[belt_nr] + j;
4273 int graphic = el2img(element);
4275 if (game.belt_dir[i] == MV_LEFT)
4276 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4278 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4283 SCAN_PLAYFIELD(x, y)
4285 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4288 int element = Feld[x][y];
4290 for (i = 0; i < NUM_BELTS; i++)
4292 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4294 int e_belt_nr = getBeltNrFromBeltElement(element);
4297 if (e_belt_nr == belt_nr)
4299 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4301 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4308 static void ToggleBeltSwitch(int x, int y)
4310 static int belt_base_element[4] =
4312 EL_CONVEYOR_BELT_1_LEFT,
4313 EL_CONVEYOR_BELT_2_LEFT,
4314 EL_CONVEYOR_BELT_3_LEFT,
4315 EL_CONVEYOR_BELT_4_LEFT
4317 static int belt_base_active_element[4] =
4319 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4320 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4321 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4322 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4324 static int belt_base_switch_element[4] =
4326 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4327 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4328 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4329 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4331 static int belt_move_dir[4] =
4339 int element = Feld[x][y];
4340 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4341 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4342 int belt_dir = belt_move_dir[belt_dir_nr];
4345 if (!IS_BELT_SWITCH(element))
4348 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4349 game.belt_dir[belt_nr] = belt_dir;
4351 if (belt_dir_nr == 3)
4354 /* set frame order for belt animation graphic according to belt direction */
4355 for (i = 0; i < NUM_BELT_PARTS; i++)
4357 int element = belt_base_active_element[belt_nr] + i;
4358 int graphic = el2img(element);
4360 if (belt_dir == MV_LEFT)
4361 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4363 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4367 SCAN_PLAYFIELD(xx, yy)
4369 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4372 int element = Feld[xx][yy];
4374 if (IS_BELT_SWITCH(element))
4376 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4378 if (e_belt_nr == belt_nr)
4380 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4381 DrawLevelField(xx, yy);
4384 else if (IS_BELT(element) && belt_dir != MV_NONE)
4386 int e_belt_nr = getBeltNrFromBeltElement(element);
4388 if (e_belt_nr == belt_nr)
4390 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4392 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4393 DrawLevelField(xx, yy);
4396 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4398 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4400 if (e_belt_nr == belt_nr)
4402 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4404 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4405 DrawLevelField(xx, yy);
4411 static void ToggleSwitchgateSwitch(int x, int y)
4415 game.switchgate_pos = !game.switchgate_pos;
4418 SCAN_PLAYFIELD(xx, yy)
4420 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4423 int element = Feld[xx][yy];
4425 #if !USE_BOTH_SWITCHGATE_SWITCHES
4426 if (element == EL_SWITCHGATE_SWITCH_UP ||
4427 element == EL_SWITCHGATE_SWITCH_DOWN)
4429 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4430 DrawLevelField(xx, yy);
4433 if (element == EL_SWITCHGATE_SWITCH_UP)
4435 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4436 DrawLevelField(xx, yy);
4438 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4440 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4441 DrawLevelField(xx, yy);
4444 else if (element == EL_SWITCHGATE_OPEN ||
4445 element == EL_SWITCHGATE_OPENING)
4447 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4449 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4451 else if (element == EL_SWITCHGATE_CLOSED ||
4452 element == EL_SWITCHGATE_CLOSING)
4454 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4456 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4461 static int getInvisibleActiveFromInvisibleElement(int element)
4463 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4464 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4465 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4469 static int getInvisibleFromInvisibleActiveElement(int element)
4471 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4472 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4473 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4477 static void RedrawAllLightSwitchesAndInvisibleElements()
4482 SCAN_PLAYFIELD(x, y)
4484 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4487 int element = Feld[x][y];
4489 if (element == EL_LIGHT_SWITCH &&
4490 game.light_time_left > 0)
4492 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4493 DrawLevelField(x, y);
4495 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4496 game.light_time_left == 0)
4498 Feld[x][y] = EL_LIGHT_SWITCH;
4499 DrawLevelField(x, y);
4501 else if (element == EL_EMC_DRIPPER &&
4502 game.light_time_left > 0)
4504 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4505 DrawLevelField(x, y);
4507 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4508 game.light_time_left == 0)
4510 Feld[x][y] = EL_EMC_DRIPPER;
4511 DrawLevelField(x, y);
4513 else if (element == EL_INVISIBLE_STEELWALL ||
4514 element == EL_INVISIBLE_WALL ||
4515 element == EL_INVISIBLE_SAND)
4517 if (game.light_time_left > 0)
4518 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4520 DrawLevelField(x, y);
4522 /* uncrumble neighbour fields, if needed */
4523 if (element == EL_INVISIBLE_SAND)
4524 DrawLevelFieldCrumbledSandNeighbours(x, y);
4526 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4527 element == EL_INVISIBLE_WALL_ACTIVE ||
4528 element == EL_INVISIBLE_SAND_ACTIVE)
4530 if (game.light_time_left == 0)
4531 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4533 DrawLevelField(x, y);
4535 /* re-crumble neighbour fields, if needed */
4536 if (element == EL_INVISIBLE_SAND)
4537 DrawLevelFieldCrumbledSandNeighbours(x, y);
4542 static void RedrawAllInvisibleElementsForLenses()
4547 SCAN_PLAYFIELD(x, y)
4549 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4552 int element = Feld[x][y];
4554 if (element == EL_EMC_DRIPPER &&
4555 game.lenses_time_left > 0)
4557 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4558 DrawLevelField(x, y);
4560 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4561 game.lenses_time_left == 0)
4563 Feld[x][y] = EL_EMC_DRIPPER;
4564 DrawLevelField(x, y);
4566 else if (element == EL_INVISIBLE_STEELWALL ||
4567 element == EL_INVISIBLE_WALL ||
4568 element == EL_INVISIBLE_SAND)
4570 if (game.lenses_time_left > 0)
4571 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4573 DrawLevelField(x, y);
4575 /* uncrumble neighbour fields, if needed */
4576 if (element == EL_INVISIBLE_SAND)
4577 DrawLevelFieldCrumbledSandNeighbours(x, y);
4579 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4580 element == EL_INVISIBLE_WALL_ACTIVE ||
4581 element == EL_INVISIBLE_SAND_ACTIVE)
4583 if (game.lenses_time_left == 0)
4584 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4586 DrawLevelField(x, y);
4588 /* re-crumble neighbour fields, if needed */
4589 if (element == EL_INVISIBLE_SAND)
4590 DrawLevelFieldCrumbledSandNeighbours(x, y);
4595 static void RedrawAllInvisibleElementsForMagnifier()
4600 SCAN_PLAYFIELD(x, y)
4602 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4605 int element = Feld[x][y];
4607 if (element == EL_EMC_FAKE_GRASS &&
4608 game.magnify_time_left > 0)
4610 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4611 DrawLevelField(x, y);
4613 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4614 game.magnify_time_left == 0)
4616 Feld[x][y] = EL_EMC_FAKE_GRASS;
4617 DrawLevelField(x, y);
4619 else if (IS_GATE_GRAY(element) &&
4620 game.magnify_time_left > 0)
4622 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4623 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4624 IS_EM_GATE_GRAY(element) ?
4625 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4626 IS_EMC_GATE_GRAY(element) ?
4627 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4629 DrawLevelField(x, y);
4631 else if (IS_GATE_GRAY_ACTIVE(element) &&
4632 game.magnify_time_left == 0)
4634 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4635 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4636 IS_EM_GATE_GRAY_ACTIVE(element) ?
4637 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4638 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4639 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4641 DrawLevelField(x, y);
4646 static void ToggleLightSwitch(int x, int y)
4648 int element = Feld[x][y];
4650 game.light_time_left =
4651 (element == EL_LIGHT_SWITCH ?
4652 level.time_light * FRAMES_PER_SECOND : 0);
4654 RedrawAllLightSwitchesAndInvisibleElements();
4657 static void ActivateTimegateSwitch(int x, int y)
4661 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4664 SCAN_PLAYFIELD(xx, yy)
4666 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4669 int element = Feld[xx][yy];
4671 if (element == EL_TIMEGATE_CLOSED ||
4672 element == EL_TIMEGATE_CLOSING)
4674 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4675 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4679 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4681 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4682 DrawLevelField(xx, yy);
4688 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4691 void Impact(int x, int y)
4693 boolean last_line = (y == lev_fieldy - 1);
4694 boolean object_hit = FALSE;
4695 boolean impact = (last_line || object_hit);
4696 int element = Feld[x][y];
4697 int smashed = EL_STEELWALL;
4699 if (!last_line) /* check if element below was hit */
4701 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4704 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4705 MovDir[x][y + 1] != MV_DOWN ||
4706 MovPos[x][y + 1] <= TILEY / 2));
4708 /* do not smash moving elements that left the smashed field in time */
4709 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4710 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4713 #if USE_QUICKSAND_IMPACT_BUGFIX
4714 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4716 RemoveMovingField(x, y + 1);
4717 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4718 Feld[x][y + 2] = EL_ROCK;
4719 DrawLevelField(x, y + 2);
4726 smashed = MovingOrBlocked2Element(x, y + 1);
4728 impact = (last_line || object_hit);
4731 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4733 SplashAcid(x, y + 1);
4737 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4738 /* only reset graphic animation if graphic really changes after impact */
4740 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4742 ResetGfxAnimation(x, y);
4743 DrawLevelField(x, y);
4746 if (impact && CAN_EXPLODE_IMPACT(element))
4751 else if (impact && element == EL_PEARL)
4753 ResetGfxAnimation(x, y);
4755 Feld[x][y] = EL_PEARL_BREAKING;
4756 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4759 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4761 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4766 if (impact && element == EL_AMOEBA_DROP)
4768 if (object_hit && IS_PLAYER(x, y + 1))
4769 KillPlayerUnlessEnemyProtected(x, y + 1);
4770 else if (object_hit && smashed == EL_PENGUIN)
4774 Feld[x][y] = EL_AMOEBA_GROWING;
4775 Store[x][y] = EL_AMOEBA_WET;
4777 ResetRandomAnimationValue(x, y);
4782 if (object_hit) /* check which object was hit */
4784 if (CAN_PASS_MAGIC_WALL(element) &&
4785 (smashed == EL_MAGIC_WALL ||
4786 smashed == EL_BD_MAGIC_WALL))
4789 int activated_magic_wall =
4790 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4791 EL_BD_MAGIC_WALL_ACTIVE);
4793 /* activate magic wall / mill */
4795 SCAN_PLAYFIELD(xx, yy)
4797 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4799 if (Feld[xx][yy] == smashed)
4800 Feld[xx][yy] = activated_magic_wall;
4802 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4803 game.magic_wall_active = TRUE;
4805 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4806 SND_MAGIC_WALL_ACTIVATING :
4807 SND_BD_MAGIC_WALL_ACTIVATING));
4810 if (IS_PLAYER(x, y + 1))
4812 if (CAN_SMASH_PLAYER(element))
4814 KillPlayerUnlessEnemyProtected(x, y + 1);
4818 else if (smashed == EL_PENGUIN)
4820 if (CAN_SMASH_PLAYER(element))
4826 else if (element == EL_BD_DIAMOND)
4828 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4834 else if (((element == EL_SP_INFOTRON ||
4835 element == EL_SP_ZONK) &&
4836 (smashed == EL_SP_SNIKSNAK ||
4837 smashed == EL_SP_ELECTRON ||
4838 smashed == EL_SP_DISK_ORANGE)) ||
4839 (element == EL_SP_INFOTRON &&
4840 smashed == EL_SP_DISK_YELLOW))
4845 else if (CAN_SMASH_EVERYTHING(element))
4847 if (IS_CLASSIC_ENEMY(smashed) ||
4848 CAN_EXPLODE_SMASHED(smashed))
4853 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4855 if (smashed == EL_LAMP ||
4856 smashed == EL_LAMP_ACTIVE)
4861 else if (smashed == EL_NUT)
4863 Feld[x][y + 1] = EL_NUT_BREAKING;
4864 PlayLevelSound(x, y, SND_NUT_BREAKING);
4865 RaiseScoreElement(EL_NUT);
4868 else if (smashed == EL_PEARL)
4870 ResetGfxAnimation(x, y);
4872 Feld[x][y + 1] = EL_PEARL_BREAKING;
4873 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4876 else if (smashed == EL_DIAMOND)
4878 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4879 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4882 else if (IS_BELT_SWITCH(smashed))
4884 ToggleBeltSwitch(x, y + 1);
4886 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4887 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4889 ToggleSwitchgateSwitch(x, y + 1);
4891 else if (smashed == EL_LIGHT_SWITCH ||
4892 smashed == EL_LIGHT_SWITCH_ACTIVE)
4894 ToggleLightSwitch(x, y + 1);
4899 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4902 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4904 CheckElementChangeBySide(x, y + 1, smashed, element,
4905 CE_SWITCHED, CH_SIDE_TOP);
4906 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4912 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4917 /* play sound of magic wall / mill */
4919 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4920 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4922 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4923 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4924 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4925 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4930 /* play sound of object that hits the ground */
4931 if (last_line || object_hit)
4932 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4935 inline static void TurnRoundExt(int x, int y)
4947 { 0, 0 }, { 0, 0 }, { 0, 0 },
4952 int left, right, back;
4956 { MV_DOWN, MV_UP, MV_RIGHT },
4957 { MV_UP, MV_DOWN, MV_LEFT },
4959 { MV_LEFT, MV_RIGHT, MV_DOWN },
4963 { MV_RIGHT, MV_LEFT, MV_UP }
4966 int element = Feld[x][y];
4967 int move_pattern = element_info[element].move_pattern;
4969 int old_move_dir = MovDir[x][y];
4970 int left_dir = turn[old_move_dir].left;
4971 int right_dir = turn[old_move_dir].right;
4972 int back_dir = turn[old_move_dir].back;
4974 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4975 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4976 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4977 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4979 int left_x = x + left_dx, left_y = y + left_dy;
4980 int right_x = x + right_dx, right_y = y + right_dy;
4981 int move_x = x + move_dx, move_y = y + move_dy;
4985 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4987 TestIfBadThingTouchesOtherBadThing(x, y);
4989 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4990 MovDir[x][y] = right_dir;
4991 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4992 MovDir[x][y] = left_dir;
4994 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4996 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4999 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5001 TestIfBadThingTouchesOtherBadThing(x, y);
5003 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5004 MovDir[x][y] = left_dir;
5005 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5006 MovDir[x][y] = right_dir;
5008 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5010 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5013 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5015 TestIfBadThingTouchesOtherBadThing(x, y);
5017 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5018 MovDir[x][y] = left_dir;
5019 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5020 MovDir[x][y] = right_dir;
5022 if (MovDir[x][y] != old_move_dir)
5025 else if (element == EL_YAMYAM)
5027 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5028 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5030 if (can_turn_left && can_turn_right)
5031 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5032 else if (can_turn_left)
5033 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5034 else if (can_turn_right)
5035 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5037 MovDir[x][y] = back_dir;
5039 MovDelay[x][y] = 16 + 16 * RND(3);
5041 else if (element == EL_DARK_YAMYAM)
5043 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5045 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5048 if (can_turn_left && can_turn_right)
5049 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5050 else if (can_turn_left)
5051 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5052 else if (can_turn_right)
5053 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5055 MovDir[x][y] = back_dir;
5057 MovDelay[x][y] = 16 + 16 * RND(3);
5059 else if (element == EL_PACMAN)
5061 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5062 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5064 if (can_turn_left && can_turn_right)
5065 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5066 else if (can_turn_left)
5067 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5068 else if (can_turn_right)
5069 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5071 MovDir[x][y] = back_dir;
5073 MovDelay[x][y] = 6 + RND(40);
5075 else if (element == EL_PIG)
5077 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5078 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5079 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5080 boolean should_turn_left, should_turn_right, should_move_on;
5082 int rnd = RND(rnd_value);
5084 should_turn_left = (can_turn_left &&
5086 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5087 y + back_dy + left_dy)));
5088 should_turn_right = (can_turn_right &&
5090 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5091 y + back_dy + right_dy)));
5092 should_move_on = (can_move_on &&
5095 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5096 y + move_dy + left_dy) ||
5097 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5098 y + move_dy + right_dy)));
5100 if (should_turn_left || should_turn_right || should_move_on)
5102 if (should_turn_left && should_turn_right && should_move_on)
5103 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5104 rnd < 2 * rnd_value / 3 ? right_dir :
5106 else if (should_turn_left && should_turn_right)
5107 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5108 else if (should_turn_left && should_move_on)
5109 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5110 else if (should_turn_right && should_move_on)
5111 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5112 else if (should_turn_left)
5113 MovDir[x][y] = left_dir;
5114 else if (should_turn_right)
5115 MovDir[x][y] = right_dir;
5116 else if (should_move_on)
5117 MovDir[x][y] = old_move_dir;
5119 else if (can_move_on && rnd > rnd_value / 8)
5120 MovDir[x][y] = old_move_dir;
5121 else if (can_turn_left && can_turn_right)
5122 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5123 else if (can_turn_left && rnd > rnd_value / 8)
5124 MovDir[x][y] = left_dir;
5125 else if (can_turn_right && rnd > rnd_value/8)
5126 MovDir[x][y] = right_dir;
5128 MovDir[x][y] = back_dir;
5130 xx = x + move_xy[MovDir[x][y]].dx;
5131 yy = y + move_xy[MovDir[x][y]].dy;
5133 if (!IN_LEV_FIELD(xx, yy) ||
5134 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5135 MovDir[x][y] = old_move_dir;
5139 else if (element == EL_DRAGON)
5141 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5142 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5143 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5145 int rnd = RND(rnd_value);
5147 if (can_move_on && rnd > rnd_value / 8)
5148 MovDir[x][y] = old_move_dir;
5149 else if (can_turn_left && can_turn_right)
5150 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5151 else if (can_turn_left && rnd > rnd_value / 8)
5152 MovDir[x][y] = left_dir;
5153 else if (can_turn_right && rnd > rnd_value / 8)
5154 MovDir[x][y] = right_dir;
5156 MovDir[x][y] = back_dir;
5158 xx = x + move_xy[MovDir[x][y]].dx;
5159 yy = y + move_xy[MovDir[x][y]].dy;
5161 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5162 MovDir[x][y] = old_move_dir;
5166 else if (element == EL_MOLE)
5168 boolean can_move_on =
5169 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5170 IS_AMOEBOID(Feld[move_x][move_y]) ||
5171 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5174 boolean can_turn_left =
5175 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5176 IS_AMOEBOID(Feld[left_x][left_y])));
5178 boolean can_turn_right =
5179 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5180 IS_AMOEBOID(Feld[right_x][right_y])));
5182 if (can_turn_left && can_turn_right)
5183 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5184 else if (can_turn_left)
5185 MovDir[x][y] = left_dir;
5187 MovDir[x][y] = right_dir;
5190 if (MovDir[x][y] != old_move_dir)
5193 else if (element == EL_BALLOON)
5195 MovDir[x][y] = game.wind_direction;
5198 else if (element == EL_SPRING)
5200 #if USE_NEW_SPRING_BUMPER
5201 if (MovDir[x][y] & MV_HORIZONTAL)
5203 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5204 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5206 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5207 ResetGfxAnimation(move_x, move_y);
5208 DrawLevelField(move_x, move_y);
5210 MovDir[x][y] = back_dir;
5212 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5213 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5214 MovDir[x][y] = MV_NONE;
5217 if (MovDir[x][y] & MV_HORIZONTAL &&
5218 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5219 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5220 MovDir[x][y] = MV_NONE;
5225 else if (element == EL_ROBOT ||
5226 element == EL_SATELLITE ||
5227 element == EL_PENGUIN ||
5228 element == EL_EMC_ANDROID)
5230 int attr_x = -1, attr_y = -1;
5241 for (i = 0; i < MAX_PLAYERS; i++)
5243 struct PlayerInfo *player = &stored_player[i];
5244 int jx = player->jx, jy = player->jy;
5246 if (!player->active)
5250 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5258 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5259 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5260 game.engine_version < VERSION_IDENT(3,1,0,0)))
5266 if (element == EL_PENGUIN)
5269 static int xy[4][2] =
5277 for (i = 0; i < NUM_DIRECTIONS; i++)
5279 int ex = x + xy[i][0];
5280 int ey = y + xy[i][1];
5282 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5291 MovDir[x][y] = MV_NONE;
5293 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5294 else if (attr_x > x)
5295 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5297 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5298 else if (attr_y > y)
5299 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5301 if (element == EL_ROBOT)
5305 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5306 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5307 Moving2Blocked(x, y, &newx, &newy);
5309 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5310 MovDelay[x][y] = 8 + 8 * !RND(3);
5312 MovDelay[x][y] = 16;
5314 else if (element == EL_PENGUIN)
5320 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5322 boolean first_horiz = RND(2);
5323 int new_move_dir = MovDir[x][y];
5326 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5327 Moving2Blocked(x, y, &newx, &newy);
5329 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5333 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5334 Moving2Blocked(x, y, &newx, &newy);
5336 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5339 MovDir[x][y] = old_move_dir;
5343 else if (element == EL_SATELLITE)
5349 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5351 boolean first_horiz = RND(2);
5352 int new_move_dir = MovDir[x][y];
5355 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5356 Moving2Blocked(x, y, &newx, &newy);
5358 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5362 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5363 Moving2Blocked(x, y, &newx, &newy);
5365 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5368 MovDir[x][y] = old_move_dir;
5372 else if (element == EL_EMC_ANDROID)
5374 static int check_pos[16] =
5376 -1, /* 0 => (invalid) */
5377 7, /* 1 => MV_LEFT */
5378 3, /* 2 => MV_RIGHT */
5379 -1, /* 3 => (invalid) */
5381 0, /* 5 => MV_LEFT | MV_UP */
5382 2, /* 6 => MV_RIGHT | MV_UP */
5383 -1, /* 7 => (invalid) */
5384 5, /* 8 => MV_DOWN */
5385 6, /* 9 => MV_LEFT | MV_DOWN */
5386 4, /* 10 => MV_RIGHT | MV_DOWN */
5387 -1, /* 11 => (invalid) */
5388 -1, /* 12 => (invalid) */
5389 -1, /* 13 => (invalid) */
5390 -1, /* 14 => (invalid) */
5391 -1, /* 15 => (invalid) */
5399 { -1, -1, MV_LEFT | MV_UP },
5401 { +1, -1, MV_RIGHT | MV_UP },
5402 { +1, 0, MV_RIGHT },
5403 { +1, +1, MV_RIGHT | MV_DOWN },
5405 { -1, +1, MV_LEFT | MV_DOWN },
5408 int start_pos, check_order;
5409 boolean can_clone = FALSE;
5412 /* check if there is any free field around current position */
5413 for (i = 0; i < 8; i++)
5415 int newx = x + check_xy[i].dx;
5416 int newy = y + check_xy[i].dy;
5418 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5426 if (can_clone) /* randomly find an element to clone */
5430 start_pos = check_pos[RND(8)];
5431 check_order = (RND(2) ? -1 : +1);
5433 for (i = 0; i < 8; i++)
5435 int pos_raw = start_pos + i * check_order;
5436 int pos = (pos_raw + 8) % 8;
5437 int newx = x + check_xy[pos].dx;
5438 int newy = y + check_xy[pos].dy;
5440 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5442 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5443 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5445 Store[x][y] = Feld[newx][newy];
5454 if (can_clone) /* randomly find a direction to move */
5458 start_pos = check_pos[RND(8)];
5459 check_order = (RND(2) ? -1 : +1);
5461 for (i = 0; i < 8; i++)
5463 int pos_raw = start_pos + i * check_order;
5464 int pos = (pos_raw + 8) % 8;
5465 int newx = x + check_xy[pos].dx;
5466 int newy = y + check_xy[pos].dy;
5467 int new_move_dir = check_xy[pos].dir;
5469 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5471 MovDir[x][y] = new_move_dir;
5472 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5481 if (can_clone) /* cloning and moving successful */
5484 /* cannot clone -- try to move towards player */
5486 start_pos = check_pos[MovDir[x][y] & 0x0f];
5487 check_order = (RND(2) ? -1 : +1);
5489 for (i = 0; i < 3; i++)
5491 /* first check start_pos, then previous/next or (next/previous) pos */
5492 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5493 int pos = (pos_raw + 8) % 8;
5494 int newx = x + check_xy[pos].dx;
5495 int newy = y + check_xy[pos].dy;
5496 int new_move_dir = check_xy[pos].dir;
5498 if (IS_PLAYER(newx, newy))
5501 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5503 MovDir[x][y] = new_move_dir;
5504 MovDelay[x][y] = level.android_move_time * 8 + 1;
5511 else if (move_pattern == MV_TURNING_LEFT ||
5512 move_pattern == MV_TURNING_RIGHT ||
5513 move_pattern == MV_TURNING_LEFT_RIGHT ||
5514 move_pattern == MV_TURNING_RIGHT_LEFT ||
5515 move_pattern == MV_TURNING_RANDOM ||
5516 move_pattern == MV_ALL_DIRECTIONS)
5518 boolean can_turn_left =
5519 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5520 boolean can_turn_right =
5521 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5523 if (element_info[element].move_stepsize == 0) /* "not moving" */
5526 if (move_pattern == MV_TURNING_LEFT)
5527 MovDir[x][y] = left_dir;
5528 else if (move_pattern == MV_TURNING_RIGHT)
5529 MovDir[x][y] = right_dir;
5530 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5531 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5532 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5533 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5534 else if (move_pattern == MV_TURNING_RANDOM)
5535 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5536 can_turn_right && !can_turn_left ? right_dir :
5537 RND(2) ? left_dir : right_dir);
5538 else if (can_turn_left && can_turn_right)
5539 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5540 else if (can_turn_left)
5541 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5542 else if (can_turn_right)
5543 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5545 MovDir[x][y] = back_dir;
5547 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5549 else if (move_pattern == MV_HORIZONTAL ||
5550 move_pattern == MV_VERTICAL)
5552 if (move_pattern & old_move_dir)
5553 MovDir[x][y] = back_dir;
5554 else if (move_pattern == MV_HORIZONTAL)
5555 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5556 else if (move_pattern == MV_VERTICAL)
5557 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5559 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5561 else if (move_pattern & MV_ANY_DIRECTION)
5563 MovDir[x][y] = move_pattern;
5564 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5566 else if (move_pattern & MV_WIND_DIRECTION)
5568 MovDir[x][y] = game.wind_direction;
5569 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5571 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5573 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5574 MovDir[x][y] = left_dir;
5575 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5576 MovDir[x][y] = right_dir;
5578 if (MovDir[x][y] != old_move_dir)
5579 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5581 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5583 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5584 MovDir[x][y] = right_dir;
5585 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5586 MovDir[x][y] = left_dir;
5588 if (MovDir[x][y] != old_move_dir)
5589 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5591 else if (move_pattern == MV_TOWARDS_PLAYER ||
5592 move_pattern == MV_AWAY_FROM_PLAYER)
5594 int attr_x = -1, attr_y = -1;
5596 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5607 for (i = 0; i < MAX_PLAYERS; i++)
5609 struct PlayerInfo *player = &stored_player[i];
5610 int jx = player->jx, jy = player->jy;
5612 if (!player->active)
5616 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5624 MovDir[x][y] = MV_NONE;
5626 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5627 else if (attr_x > x)
5628 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5630 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5631 else if (attr_y > y)
5632 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5634 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5636 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5638 boolean first_horiz = RND(2);
5639 int new_move_dir = MovDir[x][y];
5641 if (element_info[element].move_stepsize == 0) /* "not moving" */
5643 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5644 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5650 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5651 Moving2Blocked(x, y, &newx, &newy);
5653 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5657 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5658 Moving2Blocked(x, y, &newx, &newy);
5660 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5663 MovDir[x][y] = old_move_dir;
5666 else if (move_pattern == MV_WHEN_PUSHED ||
5667 move_pattern == MV_WHEN_DROPPED)
5669 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5670 MovDir[x][y] = MV_NONE;
5674 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5676 static int test_xy[7][2] =
5686 static int test_dir[7] =
5696 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5697 int move_preference = -1000000; /* start with very low preference */
5698 int new_move_dir = MV_NONE;
5699 int start_test = RND(4);
5702 for (i = 0; i < NUM_DIRECTIONS; i++)
5704 int move_dir = test_dir[start_test + i];
5705 int move_dir_preference;
5707 xx = x + test_xy[start_test + i][0];
5708 yy = y + test_xy[start_test + i][1];
5710 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5711 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5713 new_move_dir = move_dir;
5718 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5721 move_dir_preference = -1 * RunnerVisit[xx][yy];
5722 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5723 move_dir_preference = PlayerVisit[xx][yy];
5725 if (move_dir_preference > move_preference)
5727 /* prefer field that has not been visited for the longest time */
5728 move_preference = move_dir_preference;
5729 new_move_dir = move_dir;
5731 else if (move_dir_preference == move_preference &&
5732 move_dir == old_move_dir)
5734 /* prefer last direction when all directions are preferred equally */
5735 move_preference = move_dir_preference;
5736 new_move_dir = move_dir;
5740 MovDir[x][y] = new_move_dir;
5741 if (old_move_dir != new_move_dir)
5742 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5746 static void TurnRound(int x, int y)
5748 int direction = MovDir[x][y];
5750 int element, graphic;
5755 GfxDir[x][y] = MovDir[x][y];
5757 if (direction != MovDir[x][y])
5761 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5764 ResetGfxFrame(x, y, FALSE);
5766 element = Feld[x][y];
5767 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5769 if (graphic_info[graphic].anim_global_sync)
5770 GfxFrame[x][y] = FrameCounter;
5771 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5772 GfxFrame[x][y] = CustomValue[x][y];
5773 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5774 GfxFrame[x][y] = element_info[element].collect_score;
5778 static boolean JustBeingPushed(int x, int y)
5782 for (i = 0; i < MAX_PLAYERS; i++)
5784 struct PlayerInfo *player = &stored_player[i];
5786 if (player->active && player->is_pushing && player->MovPos)
5788 int next_jx = player->jx + (player->jx - player->last_jx);
5789 int next_jy = player->jy + (player->jy - player->last_jy);
5791 if (x == next_jx && y == next_jy)
5799 void StartMoving(int x, int y)
5801 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5802 int element = Feld[x][y];
5807 if (MovDelay[x][y] == 0)
5808 GfxAction[x][y] = ACTION_DEFAULT;
5810 if (CAN_FALL(element) && y < lev_fieldy - 1)
5812 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5813 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5814 if (JustBeingPushed(x, y))
5817 if (element == EL_QUICKSAND_FULL)
5819 if (IS_FREE(x, y + 1))
5821 InitMovingField(x, y, MV_DOWN);
5822 started_moving = TRUE;
5824 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5825 Store[x][y] = EL_ROCK;
5827 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5829 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5831 if (!MovDelay[x][y])
5832 MovDelay[x][y] = TILEY + 1;
5841 Feld[x][y] = EL_QUICKSAND_EMPTY;
5842 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5843 Store[x][y + 1] = Store[x][y];
5846 PlayLevelSoundAction(x, y, ACTION_FILLING);
5849 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5850 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5852 InitMovingField(x, y, MV_DOWN);
5853 started_moving = TRUE;
5855 Feld[x][y] = EL_QUICKSAND_FILLING;
5856 Store[x][y] = element;
5858 PlayLevelSoundAction(x, y, ACTION_FILLING);
5860 else if (element == EL_MAGIC_WALL_FULL)
5862 if (IS_FREE(x, y + 1))
5864 InitMovingField(x, y, MV_DOWN);
5865 started_moving = TRUE;
5867 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5868 Store[x][y] = EL_CHANGED(Store[x][y]);
5870 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5872 if (!MovDelay[x][y])
5873 MovDelay[x][y] = TILEY/4 + 1;
5882 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5883 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5884 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5888 else if (element == EL_BD_MAGIC_WALL_FULL)
5890 if (IS_FREE(x, y + 1))
5892 InitMovingField(x, y, MV_DOWN);
5893 started_moving = TRUE;
5895 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5896 Store[x][y] = EL_CHANGED2(Store[x][y]);
5898 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5900 if (!MovDelay[x][y])
5901 MovDelay[x][y] = TILEY/4 + 1;
5910 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5911 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5912 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5916 else if (CAN_PASS_MAGIC_WALL(element) &&
5917 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5918 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5920 InitMovingField(x, y, MV_DOWN);
5921 started_moving = TRUE;
5924 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5925 EL_BD_MAGIC_WALL_FILLING);
5926 Store[x][y] = element;
5928 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5930 SplashAcid(x, y + 1);
5932 InitMovingField(x, y, MV_DOWN);
5933 started_moving = TRUE;
5935 Store[x][y] = EL_ACID;
5937 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5938 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5940 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5941 CAN_FALL(element) && WasJustFalling[x][y] &&
5942 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5944 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5945 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5946 (Feld[x][y + 1] == EL_BLOCKED)))
5948 /* this is needed for a special case not covered by calling "Impact()"
5949 from "ContinueMoving()": if an element moves to a tile directly below
5950 another element which was just falling on that tile (which was empty
5951 in the previous frame), the falling element above would just stop
5952 instead of smashing the element below (in previous version, the above
5953 element was just checked for "moving" instead of "falling", resulting
5954 in incorrect smashes caused by horizontal movement of the above
5955 element; also, the case of the player being the element to smash was
5956 simply not covered here... :-/ ) */
5958 CheckCollision[x][y] = 0;
5962 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5964 if (MovDir[x][y] == MV_NONE)
5966 InitMovingField(x, y, MV_DOWN);
5967 started_moving = TRUE;
5970 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5972 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5973 MovDir[x][y] = MV_DOWN;
5975 InitMovingField(x, y, MV_DOWN);
5976 started_moving = TRUE;
5978 else if (element == EL_AMOEBA_DROP)
5980 Feld[x][y] = EL_AMOEBA_GROWING;
5981 Store[x][y] = EL_AMOEBA_WET;
5983 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5984 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5985 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5986 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5988 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5989 (IS_FREE(x - 1, y + 1) ||
5990 Feld[x - 1][y + 1] == EL_ACID));
5991 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5992 (IS_FREE(x + 1, y + 1) ||
5993 Feld[x + 1][y + 1] == EL_ACID));
5994 boolean can_fall_any = (can_fall_left || can_fall_right);
5995 boolean can_fall_both = (can_fall_left && can_fall_right);
5996 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5998 #if USE_NEW_ALL_SLIPPERY
5999 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6001 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6002 can_fall_right = FALSE;
6003 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6004 can_fall_left = FALSE;
6005 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6006 can_fall_right = FALSE;
6007 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6008 can_fall_left = FALSE;
6010 can_fall_any = (can_fall_left || can_fall_right);
6011 can_fall_both = FALSE;
6014 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6016 if (slippery_type == SLIPPERY_ONLY_LEFT)
6017 can_fall_right = FALSE;
6018 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6019 can_fall_left = FALSE;
6020 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6021 can_fall_right = FALSE;
6022 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6023 can_fall_left = FALSE;
6025 can_fall_any = (can_fall_left || can_fall_right);
6026 can_fall_both = (can_fall_left && can_fall_right);
6030 #if USE_NEW_ALL_SLIPPERY
6032 #if USE_NEW_SP_SLIPPERY
6033 /* !!! better use the same properties as for custom elements here !!! */
6034 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6035 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6037 can_fall_right = FALSE; /* slip down on left side */
6038 can_fall_both = FALSE;
6043 #if USE_NEW_ALL_SLIPPERY
6046 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6047 can_fall_right = FALSE; /* slip down on left side */
6049 can_fall_left = !(can_fall_right = RND(2));
6051 can_fall_both = FALSE;
6056 if (game.emulation == EMU_BOULDERDASH ||
6057 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6058 can_fall_right = FALSE; /* slip down on left side */
6060 can_fall_left = !(can_fall_right = RND(2));
6062 can_fall_both = FALSE;
6068 /* if not determined otherwise, prefer left side for slipping down */
6069 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6070 started_moving = TRUE;
6074 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6076 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6079 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6080 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6081 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6082 int belt_dir = game.belt_dir[belt_nr];
6084 if ((belt_dir == MV_LEFT && left_is_free) ||
6085 (belt_dir == MV_RIGHT && right_is_free))
6087 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6089 InitMovingField(x, y, belt_dir);
6090 started_moving = TRUE;
6092 Pushed[x][y] = TRUE;
6093 Pushed[nextx][y] = TRUE;
6095 GfxAction[x][y] = ACTION_DEFAULT;
6099 MovDir[x][y] = 0; /* if element was moving, stop it */
6104 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6106 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6108 if (CAN_MOVE(element) && !started_moving)
6111 int move_pattern = element_info[element].move_pattern;
6116 if (MovDir[x][y] == MV_NONE)
6118 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6119 x, y, element, element_info[element].token_name);
6120 printf("StartMoving(): This should never happen!\n");
6125 Moving2Blocked(x, y, &newx, &newy);
6127 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6130 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6131 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6133 WasJustMoving[x][y] = 0;
6134 CheckCollision[x][y] = 0;
6136 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6138 if (Feld[x][y] != element) /* element has changed */
6142 if (!MovDelay[x][y]) /* start new movement phase */
6144 /* all objects that can change their move direction after each step
6145 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6147 if (element != EL_YAMYAM &&
6148 element != EL_DARK_YAMYAM &&
6149 element != EL_PACMAN &&
6150 !(move_pattern & MV_ANY_DIRECTION) &&
6151 move_pattern != MV_TURNING_LEFT &&
6152 move_pattern != MV_TURNING_RIGHT &&
6153 move_pattern != MV_TURNING_LEFT_RIGHT &&
6154 move_pattern != MV_TURNING_RIGHT_LEFT &&
6155 move_pattern != MV_TURNING_RANDOM)
6159 if (MovDelay[x][y] && (element == EL_BUG ||
6160 element == EL_SPACESHIP ||
6161 element == EL_SP_SNIKSNAK ||
6162 element == EL_SP_ELECTRON ||
6163 element == EL_MOLE))
6164 DrawLevelField(x, y);
6168 if (MovDelay[x][y]) /* wait some time before next movement */
6172 if (element == EL_ROBOT ||
6173 element == EL_YAMYAM ||
6174 element == EL_DARK_YAMYAM)
6176 DrawLevelElementAnimationIfNeeded(x, y, element);
6177 PlayLevelSoundAction(x, y, ACTION_WAITING);
6179 else if (element == EL_SP_ELECTRON)
6180 DrawLevelElementAnimationIfNeeded(x, y, element);
6181 else if (element == EL_DRAGON)
6184 int dir = MovDir[x][y];
6185 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6186 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6187 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6188 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6189 dir == MV_UP ? IMG_FLAMES_1_UP :
6190 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6191 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6193 GfxAction[x][y] = ACTION_ATTACKING;
6195 if (IS_PLAYER(x, y))
6196 DrawPlayerField(x, y);
6198 DrawLevelField(x, y);
6200 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6202 for (i = 1; i <= 3; i++)
6204 int xx = x + i * dx;
6205 int yy = y + i * dy;
6206 int sx = SCREENX(xx);
6207 int sy = SCREENY(yy);
6208 int flame_graphic = graphic + (i - 1);
6210 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6215 int flamed = MovingOrBlocked2Element(xx, yy);
6219 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6221 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6222 RemoveMovingField(xx, yy);
6224 RemoveField(xx, yy);
6226 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6229 RemoveMovingField(xx, yy);
6232 ChangeDelay[xx][yy] = 0;
6234 Feld[xx][yy] = EL_FLAMES;
6236 if (IN_SCR_FIELD(sx, sy))
6238 DrawLevelFieldCrumbledSand(xx, yy);
6239 DrawGraphic(sx, sy, flame_graphic, frame);
6244 if (Feld[xx][yy] == EL_FLAMES)
6245 Feld[xx][yy] = EL_EMPTY;
6246 DrawLevelField(xx, yy);
6251 if (MovDelay[x][y]) /* element still has to wait some time */
6253 PlayLevelSoundAction(x, y, ACTION_WAITING);
6259 /* now make next step */
6261 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6263 if (DONT_COLLIDE_WITH(element) &&
6264 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6265 !PLAYER_ENEMY_PROTECTED(newx, newy))
6267 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6272 else if (CAN_MOVE_INTO_ACID(element) &&
6273 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6274 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6275 (MovDir[x][y] == MV_DOWN ||
6276 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6278 SplashAcid(newx, newy);
6279 Store[x][y] = EL_ACID;
6281 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6283 if (Feld[newx][newy] == EL_EXIT_OPEN)
6286 DrawLevelField(x, y);
6288 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6289 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6290 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6292 local_player->friends_still_needed--;
6293 if (!local_player->friends_still_needed &&
6294 !local_player->GameOver && AllPlayersGone)
6295 local_player->LevelSolved = local_player->GameOver = TRUE;
6299 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6301 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6302 DrawLevelField(newx, newy);
6304 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6306 else if (!IS_FREE(newx, newy))
6308 GfxAction[x][y] = ACTION_WAITING;
6310 if (IS_PLAYER(x, y))
6311 DrawPlayerField(x, y);
6313 DrawLevelField(x, y);
6318 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6320 if (IS_FOOD_PIG(Feld[newx][newy]))
6322 if (IS_MOVING(newx, newy))
6323 RemoveMovingField(newx, newy);
6326 Feld[newx][newy] = EL_EMPTY;
6327 DrawLevelField(newx, newy);
6330 PlayLevelSound(x, y, SND_PIG_DIGGING);
6332 else if (!IS_FREE(newx, newy))
6334 if (IS_PLAYER(x, y))
6335 DrawPlayerField(x, y);
6337 DrawLevelField(x, y);
6342 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6344 if (Store[x][y] != EL_EMPTY)
6346 boolean can_clone = FALSE;
6349 /* check if element to clone is still there */
6350 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6352 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6360 /* cannot clone or target field not free anymore -- do not clone */
6361 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6362 Store[x][y] = EL_EMPTY;
6365 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6367 if (IS_MV_DIAGONAL(MovDir[x][y]))
6369 int diagonal_move_dir = MovDir[x][y];
6370 int stored = Store[x][y];
6371 int change_delay = 8;
6374 /* android is moving diagonally */
6376 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6378 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6379 GfxElement[x][y] = EL_EMC_ANDROID;
6380 GfxAction[x][y] = ACTION_SHRINKING;
6381 GfxDir[x][y] = diagonal_move_dir;
6382 ChangeDelay[x][y] = change_delay;
6384 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6387 DrawLevelGraphicAnimation(x, y, graphic);
6388 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6390 if (Feld[newx][newy] == EL_ACID)
6392 SplashAcid(newx, newy);
6397 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6399 Store[newx][newy] = EL_EMC_ANDROID;
6400 GfxElement[newx][newy] = EL_EMC_ANDROID;
6401 GfxAction[newx][newy] = ACTION_GROWING;
6402 GfxDir[newx][newy] = diagonal_move_dir;
6403 ChangeDelay[newx][newy] = change_delay;
6405 graphic = el_act_dir2img(GfxElement[newx][newy],
6406 GfxAction[newx][newy], GfxDir[newx][newy]);
6408 DrawLevelGraphicAnimation(newx, newy, graphic);
6409 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6415 Feld[newx][newy] = EL_EMPTY;
6416 DrawLevelField(newx, newy);
6418 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6421 else if (!IS_FREE(newx, newy))
6424 if (IS_PLAYER(x, y))
6425 DrawPlayerField(x, y);
6427 DrawLevelField(x, y);
6433 else if (IS_CUSTOM_ELEMENT(element) &&
6434 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6436 int new_element = Feld[newx][newy];
6438 if (!IS_FREE(newx, newy))
6440 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6441 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6444 /* no element can dig solid indestructible elements */
6445 if (IS_INDESTRUCTIBLE(new_element) &&
6446 !IS_DIGGABLE(new_element) &&
6447 !IS_COLLECTIBLE(new_element))
6450 if (AmoebaNr[newx][newy] &&
6451 (new_element == EL_AMOEBA_FULL ||
6452 new_element == EL_BD_AMOEBA ||
6453 new_element == EL_AMOEBA_GROWING))
6455 AmoebaCnt[AmoebaNr[newx][newy]]--;
6456 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6459 if (IS_MOVING(newx, newy))
6460 RemoveMovingField(newx, newy);
6463 RemoveField(newx, newy);
6464 DrawLevelField(newx, newy);
6467 /* if digged element was about to explode, prevent the explosion */
6468 ExplodeField[newx][newy] = EX_TYPE_NONE;
6470 PlayLevelSoundAction(x, y, action);
6473 Store[newx][newy] = EL_EMPTY;
6475 /* this makes it possible to leave the removed element again */
6476 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6477 Store[newx][newy] = new_element;
6479 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6481 int move_leave_element = element_info[element].move_leave_element;
6483 /* this makes it possible to leave the removed element again */
6484 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6485 new_element : move_leave_element);
6489 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6491 RunnerVisit[x][y] = FrameCounter;
6492 PlayerVisit[x][y] /= 8; /* expire player visit path */
6495 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6497 if (!IS_FREE(newx, newy))
6499 if (IS_PLAYER(x, y))
6500 DrawPlayerField(x, y);
6502 DrawLevelField(x, y);
6508 boolean wanna_flame = !RND(10);
6509 int dx = newx - x, dy = newy - y;
6510 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6511 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6512 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6513 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6514 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6515 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6518 IS_CLASSIC_ENEMY(element1) ||
6519 IS_CLASSIC_ENEMY(element2)) &&
6520 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6521 element1 != EL_FLAMES && element2 != EL_FLAMES)
6523 ResetGfxAnimation(x, y);
6524 GfxAction[x][y] = ACTION_ATTACKING;
6526 if (IS_PLAYER(x, y))
6527 DrawPlayerField(x, y);
6529 DrawLevelField(x, y);
6531 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6533 MovDelay[x][y] = 50;
6537 RemoveField(newx, newy);
6539 Feld[newx][newy] = EL_FLAMES;
6540 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6543 RemoveField(newx1, newy1);
6545 Feld[newx1][newy1] = EL_FLAMES;
6547 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6550 RemoveField(newx2, newy2);
6552 Feld[newx2][newy2] = EL_FLAMES;
6559 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6560 Feld[newx][newy] == EL_DIAMOND)
6562 if (IS_MOVING(newx, newy))
6563 RemoveMovingField(newx, newy);
6566 Feld[newx][newy] = EL_EMPTY;
6567 DrawLevelField(newx, newy);
6570 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6572 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6573 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6575 if (AmoebaNr[newx][newy])
6577 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6578 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6579 Feld[newx][newy] == EL_BD_AMOEBA)
6580 AmoebaCnt[AmoebaNr[newx][newy]]--;
6585 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6587 RemoveMovingField(newx, newy);
6590 if (IS_MOVING(newx, newy))
6592 RemoveMovingField(newx, newy);
6597 Feld[newx][newy] = EL_EMPTY;
6598 DrawLevelField(newx, newy);
6601 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6603 else if ((element == EL_PACMAN || element == EL_MOLE)
6604 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6606 if (AmoebaNr[newx][newy])
6608 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6609 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6610 Feld[newx][newy] == EL_BD_AMOEBA)
6611 AmoebaCnt[AmoebaNr[newx][newy]]--;
6614 if (element == EL_MOLE)
6616 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6617 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6619 ResetGfxAnimation(x, y);
6620 GfxAction[x][y] = ACTION_DIGGING;
6621 DrawLevelField(x, y);
6623 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6625 return; /* wait for shrinking amoeba */
6627 else /* element == EL_PACMAN */
6629 Feld[newx][newy] = EL_EMPTY;
6630 DrawLevelField(newx, newy);
6631 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6634 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6635 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6636 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6638 /* wait for shrinking amoeba to completely disappear */
6641 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6643 /* object was running against a wall */
6648 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6649 if (move_pattern & MV_ANY_DIRECTION &&
6650 move_pattern == MovDir[x][y])
6652 int blocking_element =
6653 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6655 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6658 element = Feld[x][y]; /* element might have changed */
6662 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6663 DrawLevelElementAnimation(x, y, element);
6665 if (DONT_TOUCH(element))
6666 TestIfBadThingTouchesPlayer(x, y);
6671 InitMovingField(x, y, MovDir[x][y]);
6673 PlayLevelSoundAction(x, y, ACTION_MOVING);
6677 ContinueMoving(x, y);
6680 void ContinueMoving(int x, int y)
6682 int element = Feld[x][y];
6683 struct ElementInfo *ei = &element_info[element];
6684 int direction = MovDir[x][y];
6685 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6686 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6687 int newx = x + dx, newy = y + dy;
6688 int stored = Store[x][y];
6689 int stored_new = Store[newx][newy];
6690 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6691 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6692 boolean last_line = (newy == lev_fieldy - 1);
6694 MovPos[x][y] += getElementMoveStepsize(x, y);
6696 if (pushed_by_player) /* special case: moving object pushed by player */
6697 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6699 if (ABS(MovPos[x][y]) < TILEX)
6701 DrawLevelField(x, y);
6703 return; /* element is still moving */
6706 /* element reached destination field */
6708 Feld[x][y] = EL_EMPTY;
6709 Feld[newx][newy] = element;
6710 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6712 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6714 element = Feld[newx][newy] = EL_ACID;
6716 else if (element == EL_MOLE)
6718 Feld[x][y] = EL_SAND;
6720 DrawLevelFieldCrumbledSandNeighbours(x, y);
6722 else if (element == EL_QUICKSAND_FILLING)
6724 element = Feld[newx][newy] = get_next_element(element);
6725 Store[newx][newy] = Store[x][y];
6727 else if (element == EL_QUICKSAND_EMPTYING)
6729 Feld[x][y] = get_next_element(element);
6730 element = Feld[newx][newy] = Store[x][y];
6732 else if (element == EL_MAGIC_WALL_FILLING)
6734 element = Feld[newx][newy] = get_next_element(element);
6735 if (!game.magic_wall_active)
6736 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6737 Store[newx][newy] = Store[x][y];
6739 else if (element == EL_MAGIC_WALL_EMPTYING)
6741 Feld[x][y] = get_next_element(element);
6742 if (!game.magic_wall_active)
6743 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6744 element = Feld[newx][newy] = Store[x][y];
6746 #if USE_NEW_CUSTOM_VALUE
6747 InitField(newx, newy, FALSE);
6750 else if (element == EL_BD_MAGIC_WALL_FILLING)
6752 element = Feld[newx][newy] = get_next_element(element);
6753 if (!game.magic_wall_active)
6754 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6755 Store[newx][newy] = Store[x][y];
6757 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6759 Feld[x][y] = get_next_element(element);
6760 if (!game.magic_wall_active)
6761 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6762 element = Feld[newx][newy] = Store[x][y];
6764 #if USE_NEW_CUSTOM_VALUE
6765 InitField(newx, newy, FALSE);
6768 else if (element == EL_AMOEBA_DROPPING)
6770 Feld[x][y] = get_next_element(element);
6771 element = Feld[newx][newy] = Store[x][y];
6773 else if (element == EL_SOKOBAN_OBJECT)
6776 Feld[x][y] = Back[x][y];
6778 if (Back[newx][newy])
6779 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6781 Back[x][y] = Back[newx][newy] = 0;
6784 Store[x][y] = EL_EMPTY;
6789 MovDelay[newx][newy] = 0;
6792 if (CAN_CHANGE_OR_HAS_ACTION(element))
6794 if (CAN_CHANGE(element))
6797 /* copy element change control values to new field */
6798 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6799 ChangePage[newx][newy] = ChangePage[x][y];
6800 ChangeCount[newx][newy] = ChangeCount[x][y];
6801 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6804 #if USE_NEW_CUSTOM_VALUE
6805 CustomValue[newx][newy] = CustomValue[x][y];
6811 #if USE_NEW_CUSTOM_VALUE
6812 CustomValue[newx][newy] = CustomValue[x][y];
6816 ChangeDelay[x][y] = 0;
6817 ChangePage[x][y] = -1;
6818 ChangeCount[x][y] = 0;
6819 ChangeEvent[x][y] = -1;
6821 #if USE_NEW_CUSTOM_VALUE
6822 CustomValue[x][y] = 0;
6825 /* copy animation control values to new field */
6826 GfxFrame[newx][newy] = GfxFrame[x][y];
6827 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6828 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6829 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6831 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6833 /* some elements can leave other elements behind after moving */
6835 if (ei->move_leave_element != EL_EMPTY &&
6836 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6837 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6839 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6840 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6841 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6844 int move_leave_element = ei->move_leave_element;
6848 /* this makes it possible to leave the removed element again */
6849 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6850 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6852 /* this makes it possible to leave the removed element again */
6853 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6854 move_leave_element = stored;
6857 /* this makes it possible to leave the removed element again */
6858 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6859 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6860 move_leave_element = stored;
6863 Feld[x][y] = move_leave_element;
6865 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6866 MovDir[x][y] = direction;
6868 InitField(x, y, FALSE);
6870 if (GFX_CRUMBLED(Feld[x][y]))
6871 DrawLevelFieldCrumbledSandNeighbours(x, y);
6873 if (ELEM_IS_PLAYER(move_leave_element))
6874 RelocatePlayer(x, y, move_leave_element);
6877 /* do this after checking for left-behind element */
6878 ResetGfxAnimation(x, y); /* reset animation values for old field */
6880 if (!CAN_MOVE(element) ||
6881 (CAN_FALL(element) && direction == MV_DOWN &&
6882 (element == EL_SPRING ||
6883 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6884 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6885 GfxDir[x][y] = MovDir[newx][newy] = 0;
6887 DrawLevelField(x, y);
6888 DrawLevelField(newx, newy);
6890 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6892 /* prevent pushed element from moving on in pushed direction */
6893 if (pushed_by_player && CAN_MOVE(element) &&
6894 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6895 !(element_info[element].move_pattern & direction))
6896 TurnRound(newx, newy);
6898 /* prevent elements on conveyor belt from moving on in last direction */
6899 if (pushed_by_conveyor && CAN_FALL(element) &&
6900 direction & MV_HORIZONTAL)
6901 MovDir[newx][newy] = 0;
6903 if (!pushed_by_player)
6905 int nextx = newx + dx, nexty = newy + dy;
6906 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6908 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6910 if (CAN_FALL(element) && direction == MV_DOWN)
6911 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6913 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6914 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6917 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6919 TestIfBadThingTouchesPlayer(newx, newy);
6920 TestIfBadThingTouchesFriend(newx, newy);
6922 if (!IS_CUSTOM_ELEMENT(element))
6923 TestIfBadThingTouchesOtherBadThing(newx, newy);
6925 else if (element == EL_PENGUIN)
6926 TestIfFriendTouchesBadThing(newx, newy);
6928 /* give the player one last chance (one more frame) to move away */
6929 if (CAN_FALL(element) && direction == MV_DOWN &&
6930 (last_line || (!IS_FREE(x, newy + 1) &&
6931 (!IS_PLAYER(x, newy + 1) ||
6932 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6935 if (pushed_by_player && !game.use_change_when_pushing_bug)
6937 int push_side = MV_DIR_OPPOSITE(direction);
6938 struct PlayerInfo *player = PLAYERINFO(x, y);
6940 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6941 player->index_bit, push_side);
6942 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6943 player->index_bit, push_side);
6946 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6947 MovDelay[newx][newy] = 1;
6949 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6951 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6954 if (ChangePage[newx][newy] != -1) /* delayed change */
6956 int page = ChangePage[newx][newy];
6957 struct ElementChangeInfo *change = &ei->change_page[page];
6959 ChangePage[newx][newy] = -1;
6961 if (change->can_change)
6963 if (ChangeElement(newx, newy, element, page))
6965 if (change->post_change_function)
6966 change->post_change_function(newx, newy);
6970 if (change->has_action)
6971 ExecuteCustomElementAction(newx, newy, element, page);
6975 TestIfElementHitsCustomElement(newx, newy, direction);
6976 TestIfPlayerTouchesCustomElement(newx, newy);
6977 TestIfElementTouchesCustomElement(newx, newy);
6980 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6981 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6982 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6983 MV_DIR_OPPOSITE(direction));
6987 int AmoebeNachbarNr(int ax, int ay)
6990 int element = Feld[ax][ay];
6992 static int xy[4][2] =
7000 for (i = 0; i < NUM_DIRECTIONS; i++)
7002 int x = ax + xy[i][0];
7003 int y = ay + xy[i][1];
7005 if (!IN_LEV_FIELD(x, y))
7008 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7009 group_nr = AmoebaNr[x][y];
7015 void AmoebenVereinigen(int ax, int ay)
7017 int i, x, y, xx, yy;
7018 int new_group_nr = AmoebaNr[ax][ay];
7019 static int xy[4][2] =
7027 if (new_group_nr == 0)
7030 for (i = 0; i < NUM_DIRECTIONS; i++)
7035 if (!IN_LEV_FIELD(x, y))
7038 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7039 Feld[x][y] == EL_BD_AMOEBA ||
7040 Feld[x][y] == EL_AMOEBA_DEAD) &&
7041 AmoebaNr[x][y] != new_group_nr)
7043 int old_group_nr = AmoebaNr[x][y];
7045 if (old_group_nr == 0)
7048 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7049 AmoebaCnt[old_group_nr] = 0;
7050 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7051 AmoebaCnt2[old_group_nr] = 0;
7054 SCAN_PLAYFIELD(xx, yy)
7056 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7059 if (AmoebaNr[xx][yy] == old_group_nr)
7060 AmoebaNr[xx][yy] = new_group_nr;
7066 void AmoebeUmwandeln(int ax, int ay)
7070 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7072 int group_nr = AmoebaNr[ax][ay];
7077 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7078 printf("AmoebeUmwandeln(): This should never happen!\n");
7084 SCAN_PLAYFIELD(x, y)
7086 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7089 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7092 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7096 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7097 SND_AMOEBA_TURNING_TO_GEM :
7098 SND_AMOEBA_TURNING_TO_ROCK));
7103 static int xy[4][2] =
7111 for (i = 0; i < NUM_DIRECTIONS; i++)
7116 if (!IN_LEV_FIELD(x, y))
7119 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7121 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7122 SND_AMOEBA_TURNING_TO_GEM :
7123 SND_AMOEBA_TURNING_TO_ROCK));
7130 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7133 int group_nr = AmoebaNr[ax][ay];
7134 boolean done = FALSE;
7139 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7140 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7146 SCAN_PLAYFIELD(x, y)
7148 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7151 if (AmoebaNr[x][y] == group_nr &&
7152 (Feld[x][y] == EL_AMOEBA_DEAD ||
7153 Feld[x][y] == EL_BD_AMOEBA ||
7154 Feld[x][y] == EL_AMOEBA_GROWING))
7157 Feld[x][y] = new_element;
7158 InitField(x, y, FALSE);
7159 DrawLevelField(x, y);
7165 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7166 SND_BD_AMOEBA_TURNING_TO_ROCK :
7167 SND_BD_AMOEBA_TURNING_TO_GEM));
7170 void AmoebeWaechst(int x, int y)
7172 static unsigned long sound_delay = 0;
7173 static unsigned long sound_delay_value = 0;
7175 if (!MovDelay[x][y]) /* start new growing cycle */
7179 if (DelayReached(&sound_delay, sound_delay_value))
7181 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7182 sound_delay_value = 30;
7186 if (MovDelay[x][y]) /* wait some time before growing bigger */
7189 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7191 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7192 6 - MovDelay[x][y]);
7194 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7197 if (!MovDelay[x][y])
7199 Feld[x][y] = Store[x][y];
7201 DrawLevelField(x, y);
7206 void AmoebaDisappearing(int x, int y)
7208 static unsigned long sound_delay = 0;
7209 static unsigned long sound_delay_value = 0;
7211 if (!MovDelay[x][y]) /* start new shrinking cycle */
7215 if (DelayReached(&sound_delay, sound_delay_value))
7216 sound_delay_value = 30;
7219 if (MovDelay[x][y]) /* wait some time before shrinking */
7222 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7224 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7225 6 - MovDelay[x][y]);
7227 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7230 if (!MovDelay[x][y])
7232 Feld[x][y] = EL_EMPTY;
7233 DrawLevelField(x, y);
7235 /* don't let mole enter this field in this cycle;
7236 (give priority to objects falling to this field from above) */
7242 void AmoebeAbleger(int ax, int ay)
7245 int element = Feld[ax][ay];
7246 int graphic = el2img(element);
7247 int newax = ax, neway = ay;
7248 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7249 static int xy[4][2] =
7257 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7259 Feld[ax][ay] = EL_AMOEBA_DEAD;
7260 DrawLevelField(ax, ay);
7264 if (IS_ANIMATED(graphic))
7265 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7267 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7268 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7270 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7273 if (MovDelay[ax][ay])
7277 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7280 int x = ax + xy[start][0];
7281 int y = ay + xy[start][1];
7283 if (!IN_LEV_FIELD(x, y))
7286 if (IS_FREE(x, y) ||
7287 CAN_GROW_INTO(Feld[x][y]) ||
7288 Feld[x][y] == EL_QUICKSAND_EMPTY)
7294 if (newax == ax && neway == ay)
7297 else /* normal or "filled" (BD style) amoeba */
7300 boolean waiting_for_player = FALSE;
7302 for (i = 0; i < NUM_DIRECTIONS; i++)
7304 int j = (start + i) % 4;
7305 int x = ax + xy[j][0];
7306 int y = ay + xy[j][1];
7308 if (!IN_LEV_FIELD(x, y))
7311 if (IS_FREE(x, y) ||
7312 CAN_GROW_INTO(Feld[x][y]) ||
7313 Feld[x][y] == EL_QUICKSAND_EMPTY)
7319 else if (IS_PLAYER(x, y))
7320 waiting_for_player = TRUE;
7323 if (newax == ax && neway == ay) /* amoeba cannot grow */
7325 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7327 Feld[ax][ay] = EL_AMOEBA_DEAD;
7328 DrawLevelField(ax, ay);
7329 AmoebaCnt[AmoebaNr[ax][ay]]--;
7331 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7333 if (element == EL_AMOEBA_FULL)
7334 AmoebeUmwandeln(ax, ay);
7335 else if (element == EL_BD_AMOEBA)
7336 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7341 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7343 /* amoeba gets larger by growing in some direction */
7345 int new_group_nr = AmoebaNr[ax][ay];
7348 if (new_group_nr == 0)
7350 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7351 printf("AmoebeAbleger(): This should never happen!\n");
7356 AmoebaNr[newax][neway] = new_group_nr;
7357 AmoebaCnt[new_group_nr]++;
7358 AmoebaCnt2[new_group_nr]++;
7360 /* if amoeba touches other amoeba(s) after growing, unify them */
7361 AmoebenVereinigen(newax, neway);
7363 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7365 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7371 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7372 (neway == lev_fieldy - 1 && newax != ax))
7374 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7375 Store[newax][neway] = element;
7377 else if (neway == ay || element == EL_EMC_DRIPPER)
7379 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7381 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7385 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7386 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7387 Store[ax][ay] = EL_AMOEBA_DROP;
7388 ContinueMoving(ax, ay);
7392 DrawLevelField(newax, neway);
7395 void Life(int ax, int ay)
7399 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7402 int element = Feld[ax][ay];
7403 int graphic = el2img(element);
7404 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7406 boolean changed = FALSE;
7408 if (IS_ANIMATED(graphic))
7409 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7414 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7415 MovDelay[ax][ay] = life_time;
7417 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7420 if (MovDelay[ax][ay])
7424 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7426 int xx = ax+x1, yy = ay+y1;
7429 if (!IN_LEV_FIELD(xx, yy))
7432 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7434 int x = xx+x2, y = yy+y2;
7436 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7439 if (((Feld[x][y] == element ||
7440 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7442 (IS_FREE(x, y) && Stop[x][y]))
7446 if (xx == ax && yy == ay) /* field in the middle */
7448 if (nachbarn < life_parameter[0] ||
7449 nachbarn > life_parameter[1])
7451 Feld[xx][yy] = EL_EMPTY;
7453 DrawLevelField(xx, yy);
7454 Stop[xx][yy] = TRUE;
7458 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7459 { /* free border field */
7460 if (nachbarn >= life_parameter[2] &&
7461 nachbarn <= life_parameter[3])
7463 Feld[xx][yy] = element;
7464 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7466 DrawLevelField(xx, yy);
7467 Stop[xx][yy] = TRUE;
7474 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7475 SND_GAME_OF_LIFE_GROWING);
7478 static void InitRobotWheel(int x, int y)
7480 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7483 static void RunRobotWheel(int x, int y)
7485 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7488 static void StopRobotWheel(int x, int y)
7490 if (ZX == x && ZY == y)
7494 static void InitTimegateWheel(int x, int y)
7496 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7499 static void RunTimegateWheel(int x, int y)
7501 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7504 static void InitMagicBallDelay(int x, int y)
7507 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7509 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7513 static void ActivateMagicBall(int bx, int by)
7517 if (level.ball_random)
7519 int pos_border = RND(8); /* select one of the eight border elements */
7520 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7521 int xx = pos_content % 3;
7522 int yy = pos_content / 3;
7527 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7528 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7532 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7534 int xx = x - bx + 1;
7535 int yy = y - by + 1;
7537 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7538 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7542 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7545 static void InitDiagonalMovingElement(int x, int y)
7548 MovDelay[x][y] = level.android_move_time;
7552 void CheckExit(int x, int y)
7554 if (local_player->gems_still_needed > 0 ||
7555 local_player->sokobanfields_still_needed > 0 ||
7556 local_player->lights_still_needed > 0)
7558 int element = Feld[x][y];
7559 int graphic = el2img(element);
7561 if (IS_ANIMATED(graphic))
7562 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7567 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7570 Feld[x][y] = EL_EXIT_OPENING;
7572 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7575 void CheckExitSP(int x, int y)
7577 if (local_player->gems_still_needed > 0)
7579 int element = Feld[x][y];
7580 int graphic = el2img(element);
7582 if (IS_ANIMATED(graphic))
7583 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7588 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7591 Feld[x][y] = EL_SP_EXIT_OPENING;
7593 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7596 static void CloseAllOpenTimegates()
7601 SCAN_PLAYFIELD(x, y)
7603 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7606 int element = Feld[x][y];
7608 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7610 Feld[x][y] = EL_TIMEGATE_CLOSING;
7612 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7617 void EdelsteinFunkeln(int x, int y)
7619 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7622 if (Feld[x][y] == EL_BD_DIAMOND)
7625 if (MovDelay[x][y] == 0) /* next animation frame */
7626 MovDelay[x][y] = 11 * !SimpleRND(500);
7628 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7632 if (setup.direct_draw && MovDelay[x][y])
7633 SetDrawtoField(DRAW_BUFFERED);
7635 DrawLevelElementAnimation(x, y, Feld[x][y]);
7637 if (MovDelay[x][y] != 0)
7639 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7640 10 - MovDelay[x][y]);
7642 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7644 if (setup.direct_draw)
7648 dest_x = FX + SCREENX(x) * TILEX;
7649 dest_y = FY + SCREENY(y) * TILEY;
7651 BlitBitmap(drawto_field, window,
7652 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7653 SetDrawtoField(DRAW_DIRECT);
7659 void MauerWaechst(int x, int y)
7663 if (!MovDelay[x][y]) /* next animation frame */
7664 MovDelay[x][y] = 3 * delay;
7666 if (MovDelay[x][y]) /* wait some time before next frame */
7670 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7672 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7673 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7675 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7678 if (!MovDelay[x][y])
7680 if (MovDir[x][y] == MV_LEFT)
7682 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7683 DrawLevelField(x - 1, y);
7685 else if (MovDir[x][y] == MV_RIGHT)
7687 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7688 DrawLevelField(x + 1, y);
7690 else if (MovDir[x][y] == MV_UP)
7692 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7693 DrawLevelField(x, y - 1);
7697 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7698 DrawLevelField(x, y + 1);
7701 Feld[x][y] = Store[x][y];
7703 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7704 DrawLevelField(x, y);
7709 void MauerAbleger(int ax, int ay)
7711 int element = Feld[ax][ay];
7712 int graphic = el2img(element);
7713 boolean oben_frei = FALSE, unten_frei = FALSE;
7714 boolean links_frei = FALSE, rechts_frei = FALSE;
7715 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7716 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7717 boolean new_wall = FALSE;
7719 if (IS_ANIMATED(graphic))
7720 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7722 if (!MovDelay[ax][ay]) /* start building new wall */
7723 MovDelay[ax][ay] = 6;
7725 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7728 if (MovDelay[ax][ay])
7732 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7734 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7736 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7738 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7741 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7742 element == EL_EXPANDABLE_WALL_ANY)
7746 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7747 Store[ax][ay-1] = element;
7748 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7749 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7750 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7751 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7756 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7757 Store[ax][ay+1] = element;
7758 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7759 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7760 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7761 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7766 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7767 element == EL_EXPANDABLE_WALL_ANY ||
7768 element == EL_EXPANDABLE_WALL)
7772 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7773 Store[ax-1][ay] = element;
7774 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7775 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7776 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7777 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7783 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7784 Store[ax+1][ay] = element;
7785 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7786 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7787 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7788 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7793 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7794 DrawLevelField(ax, ay);
7796 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7798 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7799 unten_massiv = TRUE;
7800 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7801 links_massiv = TRUE;
7802 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7803 rechts_massiv = TRUE;
7805 if (((oben_massiv && unten_massiv) ||
7806 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7807 element == EL_EXPANDABLE_WALL) &&
7808 ((links_massiv && rechts_massiv) ||
7809 element == EL_EXPANDABLE_WALL_VERTICAL))
7810 Feld[ax][ay] = EL_WALL;
7813 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7816 void CheckForDragon(int x, int y)
7819 boolean dragon_found = FALSE;
7820 static int xy[4][2] =
7828 for (i = 0; i < NUM_DIRECTIONS; i++)
7830 for (j = 0; j < 4; j++)
7832 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7834 if (IN_LEV_FIELD(xx, yy) &&
7835 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7837 if (Feld[xx][yy] == EL_DRAGON)
7838 dragon_found = TRUE;
7847 for (i = 0; i < NUM_DIRECTIONS; i++)
7849 for (j = 0; j < 3; j++)
7851 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7853 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7855 Feld[xx][yy] = EL_EMPTY;
7856 DrawLevelField(xx, yy);
7865 static void InitBuggyBase(int x, int y)
7867 int element = Feld[x][y];
7868 int activating_delay = FRAMES_PER_SECOND / 4;
7871 (element == EL_SP_BUGGY_BASE ?
7872 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7873 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7875 element == EL_SP_BUGGY_BASE_ACTIVE ?
7876 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7879 static void WarnBuggyBase(int x, int y)
7882 static int xy[4][2] =
7890 for (i = 0; i < NUM_DIRECTIONS; i++)
7892 int xx = x + xy[i][0];
7893 int yy = y + xy[i][1];
7895 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7897 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7904 static void InitTrap(int x, int y)
7906 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7909 static void ActivateTrap(int x, int y)
7911 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7914 static void ChangeActiveTrap(int x, int y)
7916 int graphic = IMG_TRAP_ACTIVE;
7918 /* if new animation frame was drawn, correct crumbled sand border */
7919 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7920 DrawLevelFieldCrumbledSand(x, y);
7923 static int getSpecialActionElement(int element, int number, int base_element)
7925 return (element != EL_EMPTY ? element :
7926 number != -1 ? base_element + number - 1 :
7930 static int getModifiedActionNumber(int value_old, int operator, int operand,
7931 int value_min, int value_max)
7933 int value_new = (operator == CA_MODE_SET ? operand :
7934 operator == CA_MODE_ADD ? value_old + operand :
7935 operator == CA_MODE_SUBTRACT ? value_old - operand :
7936 operator == CA_MODE_MULTIPLY ? value_old * operand :
7937 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7938 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7941 return (value_new < value_min ? value_min :
7942 value_new > value_max ? value_max :
7946 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7948 struct ElementInfo *ei = &element_info[element];
7949 struct ElementChangeInfo *change = &ei->change_page[page];
7950 int target_element = change->target_element;
7951 int action_type = change->action_type;
7952 int action_mode = change->action_mode;
7953 int action_arg = change->action_arg;
7956 if (!change->has_action)
7959 /* ---------- determine action paramater values -------------------------- */
7961 int level_time_value =
7962 (level.time > 0 ? TimeLeft :
7965 int action_arg_element =
7966 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7967 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7968 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7971 int action_arg_direction =
7972 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7973 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7974 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7975 change->actual_trigger_side :
7976 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7977 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7980 int action_arg_number_min =
7981 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7984 int action_arg_number_max =
7985 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7986 action_type == CA_SET_LEVEL_GEMS ? 999 :
7987 action_type == CA_SET_LEVEL_TIME ? 9999 :
7988 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7989 action_type == CA_SET_CE_VALUE ? 9999 :
7990 action_type == CA_SET_CE_SCORE ? 9999 :
7993 int action_arg_number_reset =
7994 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7995 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7996 action_type == CA_SET_LEVEL_TIME ? level.time :
7997 action_type == CA_SET_LEVEL_SCORE ? 0 :
7999 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8001 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8003 action_type == CA_SET_CE_SCORE ? 0 :
8006 int action_arg_number =
8007 (action_arg <= CA_ARG_MAX ? action_arg :
8008 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8009 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8010 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8011 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8012 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8013 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8014 #if USE_NEW_CUSTOM_VALUE
8015 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8017 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8019 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8020 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8021 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8022 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8023 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8024 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8025 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8026 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8027 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8028 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8029 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8032 int action_arg_number_old =
8033 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8034 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8035 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8036 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8037 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8040 int action_arg_number_new =
8041 getModifiedActionNumber(action_arg_number_old,
8042 action_mode, action_arg_number,
8043 action_arg_number_min, action_arg_number_max);
8045 int trigger_player_bits =
8046 (change->actual_trigger_player >= EL_PLAYER_1 &&
8047 change->actual_trigger_player <= EL_PLAYER_4 ?
8048 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8051 int action_arg_player_bits =
8052 (action_arg >= CA_ARG_PLAYER_1 &&
8053 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8054 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8057 /* ---------- execute action -------------------------------------------- */
8066 /* ---------- level actions ------------------------------------------- */
8068 case CA_RESTART_LEVEL:
8070 game.restart_level = TRUE;
8075 case CA_SHOW_ENVELOPE:
8077 int element = getSpecialActionElement(action_arg_element,
8078 action_arg_number, EL_ENVELOPE_1);
8080 if (IS_ENVELOPE(element))
8081 local_player->show_envelope = element;
8086 case CA_SET_LEVEL_TIME:
8088 if (level.time > 0) /* only modify limited time value */
8090 TimeLeft = action_arg_number_new;
8092 DrawGameValue_Time(TimeLeft);
8094 if (!TimeLeft && setup.time_limit)
8095 for (i = 0; i < MAX_PLAYERS; i++)
8096 KillPlayer(&stored_player[i]);
8102 case CA_SET_LEVEL_SCORE:
8104 local_player->score = action_arg_number_new;
8106 DrawGameValue_Score(local_player->score);
8111 case CA_SET_LEVEL_GEMS:
8113 local_player->gems_still_needed = action_arg_number_new;
8115 DrawGameValue_Emeralds(local_player->gems_still_needed);
8120 case CA_SET_LEVEL_GRAVITY:
8122 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8123 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8124 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8129 case CA_SET_LEVEL_WIND:
8131 game.wind_direction = action_arg_direction;
8136 /* ---------- player actions ------------------------------------------ */
8138 case CA_MOVE_PLAYER:
8140 /* automatically move to the next field in specified direction */
8141 for (i = 0; i < MAX_PLAYERS; i++)
8142 if (trigger_player_bits & (1 << i))
8143 stored_player[i].programmed_action = action_arg_direction;
8148 case CA_EXIT_PLAYER:
8150 for (i = 0; i < MAX_PLAYERS; i++)
8151 if (action_arg_player_bits & (1 << i))
8152 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8157 case CA_KILL_PLAYER:
8159 for (i = 0; i < MAX_PLAYERS; i++)
8160 if (action_arg_player_bits & (1 << i))
8161 KillPlayer(&stored_player[i]);
8166 case CA_SET_PLAYER_KEYS:
8168 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8169 int element = getSpecialActionElement(action_arg_element,
8170 action_arg_number, EL_KEY_1);
8172 if (IS_KEY(element))
8174 for (i = 0; i < MAX_PLAYERS; i++)
8176 if (trigger_player_bits & (1 << i))
8178 stored_player[i].key[KEY_NR(element)] = key_state;
8181 DrawGameDoorValues();
8183 DrawGameValue_Keys(stored_player[i].key);
8186 redraw_mask |= REDRAW_DOOR_1;
8194 case CA_SET_PLAYER_SPEED:
8196 for (i = 0; i < MAX_PLAYERS; i++)
8198 if (trigger_player_bits & (1 << i))
8200 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8202 if (action_arg == CA_ARG_SPEED_FASTER &&
8203 stored_player[i].cannot_move)
8205 action_arg_number = STEPSIZE_VERY_SLOW;
8207 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8208 action_arg == CA_ARG_SPEED_FASTER)
8210 action_arg_number = 2;
8211 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8216 getModifiedActionNumber(move_stepsize,
8219 action_arg_number_min,
8220 action_arg_number_max);
8223 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8225 /* make sure that value is power of 2 */
8226 move_stepsize = (1 << log_2(move_stepsize));
8228 /* do no immediately change -- the player might just be moving */
8229 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8231 stored_player[i].cannot_move =
8232 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8240 case CA_SET_PLAYER_SHIELD:
8242 for (i = 0; i < MAX_PLAYERS; i++)
8244 if (trigger_player_bits & (1 << i))
8246 if (action_arg == CA_ARG_SHIELD_OFF)
8248 stored_player[i].shield_normal_time_left = 0;
8249 stored_player[i].shield_deadly_time_left = 0;
8251 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8253 stored_player[i].shield_normal_time_left = 999999;
8255 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8257 stored_player[i].shield_normal_time_left = 999999;
8258 stored_player[i].shield_deadly_time_left = 999999;
8266 case CA_SET_PLAYER_ARTWORK:
8268 for (i = 0; i < MAX_PLAYERS; i++)
8270 if (trigger_player_bits & (1 << i))
8272 int artwork_element = action_arg_element;
8274 if (action_arg == CA_ARG_ELEMENT_RESET)
8276 (level.use_artwork_element[i] ? level.artwork_element[i] :
8277 stored_player[i].element_nr);
8279 stored_player[i].artwork_element = artwork_element;
8281 SetPlayerWaiting(&stored_player[i], FALSE);
8283 /* set number of special actions for bored and sleeping animation */
8284 stored_player[i].num_special_action_bored =
8285 get_num_special_action(artwork_element,
8286 ACTION_BORING_1, ACTION_BORING_LAST);
8287 stored_player[i].num_special_action_sleeping =
8288 get_num_special_action(artwork_element,
8289 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8296 /* ---------- CE actions ---------------------------------------------- */
8298 case CA_SET_CE_VALUE:
8300 #if USE_NEW_CUSTOM_VALUE
8301 int last_ce_value = CustomValue[x][y];
8303 CustomValue[x][y] = action_arg_number_new;
8306 printf("::: CE value == %d\n", CustomValue[x][y]);
8309 if (CustomValue[x][y] == 0 && last_ce_value > 0)
8312 printf("::: CE_VALUE_GETS_ZERO\n");
8315 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8316 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8319 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8327 case CA_SET_CE_SCORE:
8329 #if USE_NEW_CUSTOM_VALUE
8330 int last_ce_score = ei->collect_score;
8332 ei->collect_score = action_arg_number_new;
8335 printf("::: CE score == %d\n", ei->collect_score);
8338 if (ei->collect_score == 0 && last_ce_score > 0)
8341 printf("::: CE_SCORE_GETS_ZERO\n");
8344 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8345 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8348 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8356 /* ---------- engine actions ------------------------------------------ */
8358 case CA_SET_ENGINE_SCAN_MODE:
8360 InitPlayfieldScanMode(action_arg);
8370 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8372 int old_element = Feld[x][y];
8373 int new_element = get_element_from_group_element(element);
8374 int previous_move_direction = MovDir[x][y];
8375 #if USE_NEW_CUSTOM_VALUE
8376 int last_ce_value = CustomValue[x][y];
8378 boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8379 IS_WALKABLE(old_element));
8382 /* check if element under the player changes from accessible to unaccessible
8383 (needed for special case of dropping element which then changes) */
8384 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8385 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8395 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8396 RemoveMovingField(x, y);
8400 Feld[x][y] = new_element;
8402 #if !USE_GFX_RESET_GFX_ANIMATION
8403 ResetGfxAnimation(x, y);
8404 ResetRandomAnimationValue(x, y);
8407 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8408 MovDir[x][y] = previous_move_direction;
8410 #if USE_NEW_CUSTOM_VALUE
8411 if (element_info[new_element].use_last_ce_value)
8412 CustomValue[x][y] = last_ce_value;
8415 InitField_WithBug1(x, y, FALSE);
8417 new_element = Feld[x][y]; /* element may have changed */
8419 #if USE_GFX_RESET_GFX_ANIMATION
8420 ResetGfxAnimation(x, y);
8421 ResetRandomAnimationValue(x, y);
8424 DrawLevelField(x, y);
8426 if (GFX_CRUMBLED(new_element))
8427 DrawLevelFieldCrumbledSandNeighbours(x, y);
8431 /* check if element under the player changes from accessible to unaccessible
8432 (needed for special case of dropping element which then changes) */
8433 /* (must be checked after creating new element for walkable group elements) */
8434 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8435 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8443 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8444 if (ELEM_IS_PLAYER(new_element))
8445 RelocatePlayer(x, y, new_element);
8448 ChangeCount[x][y]++; /* count number of changes in the same frame */
8450 TestIfBadThingTouchesPlayer(x, y);
8451 TestIfPlayerTouchesCustomElement(x, y);
8452 TestIfElementTouchesCustomElement(x, y);
8455 static void CreateField(int x, int y, int element)
8457 CreateFieldExt(x, y, element, FALSE);
8460 static void CreateElementFromChange(int x, int y, int element)
8462 element = GET_VALID_RUNTIME_ELEMENT(element);
8464 #if USE_STOP_CHANGED_ELEMENTS
8465 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8467 int old_element = Feld[x][y];
8469 /* prevent changed element from moving in same engine frame
8470 unless both old and new element can either fall or move */
8471 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8472 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8477 CreateFieldExt(x, y, element, TRUE);
8480 static boolean ChangeElement(int x, int y, int element, int page)
8482 struct ElementInfo *ei = &element_info[element];
8483 struct ElementChangeInfo *change = &ei->change_page[page];
8484 int ce_value = CustomValue[x][y];
8485 int ce_score = ei->collect_score;
8487 int old_element = Feld[x][y];
8489 /* always use default change event to prevent running into a loop */
8490 if (ChangeEvent[x][y] == -1)
8491 ChangeEvent[x][y] = CE_DELAY;
8493 if (ChangeEvent[x][y] == CE_DELAY)
8495 /* reset actual trigger element, trigger player and action element */
8496 change->actual_trigger_element = EL_EMPTY;
8497 change->actual_trigger_player = EL_PLAYER_1;
8498 change->actual_trigger_side = CH_SIDE_NONE;
8499 change->actual_trigger_ce_value = 0;
8500 change->actual_trigger_ce_score = 0;
8503 /* do not change elements more than a specified maximum number of changes */
8504 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8507 ChangeCount[x][y]++; /* count number of changes in the same frame */
8509 if (change->explode)
8516 if (change->use_target_content)
8518 boolean complete_replace = TRUE;
8519 boolean can_replace[3][3];
8522 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8525 boolean is_walkable;
8526 boolean is_diggable;
8527 boolean is_collectible;
8528 boolean is_removable;
8529 boolean is_destructible;
8530 int ex = x + xx - 1;
8531 int ey = y + yy - 1;
8532 int content_element = change->target_content.e[xx][yy];
8535 can_replace[xx][yy] = TRUE;
8537 if (ex == x && ey == y) /* do not check changing element itself */
8540 if (content_element == EL_EMPTY_SPACE)
8542 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8547 if (!IN_LEV_FIELD(ex, ey))
8549 can_replace[xx][yy] = FALSE;
8550 complete_replace = FALSE;
8557 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8558 e = MovingOrBlocked2Element(ex, ey);
8560 is_empty = (IS_FREE(ex, ey) ||
8561 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8563 is_walkable = (is_empty || IS_WALKABLE(e));
8564 is_diggable = (is_empty || IS_DIGGABLE(e));
8565 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8566 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8567 is_removable = (is_diggable || is_collectible);
8569 can_replace[xx][yy] =
8570 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8571 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8572 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8573 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8574 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8575 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8576 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8578 if (!can_replace[xx][yy])
8579 complete_replace = FALSE;
8582 if (!change->only_if_complete || complete_replace)
8584 boolean something_has_changed = FALSE;
8586 if (change->only_if_complete && change->use_random_replace &&
8587 RND(100) < change->random_percentage)
8590 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8592 int ex = x + xx - 1;
8593 int ey = y + yy - 1;
8594 int content_element;
8596 if (can_replace[xx][yy] && (!change->use_random_replace ||
8597 RND(100) < change->random_percentage))
8599 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8600 RemoveMovingField(ex, ey);
8602 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8604 content_element = change->target_content.e[xx][yy];
8605 target_element = GET_TARGET_ELEMENT(content_element, change,
8606 ce_value, ce_score);
8608 CreateElementFromChange(ex, ey, target_element);
8610 something_has_changed = TRUE;
8612 /* for symmetry reasons, freeze newly created border elements */
8613 if (ex != x || ey != y)
8614 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8618 if (something_has_changed)
8620 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8621 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8627 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8628 ce_value, ce_score);
8630 if (element == EL_DIAGONAL_GROWING ||
8631 element == EL_DIAGONAL_SHRINKING)
8633 target_element = Store[x][y];
8635 Store[x][y] = EL_EMPTY;
8638 CreateElementFromChange(x, y, target_element);
8640 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8641 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8644 /* this uses direct change before indirect change */
8645 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8650 #if USE_NEW_DELAYED_ACTION
8652 static void HandleElementChange(int x, int y, int page)
8654 int element = MovingOrBlocked2Element(x, y);
8655 struct ElementInfo *ei = &element_info[element];
8656 struct ElementChangeInfo *change = &ei->change_page[page];
8659 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8660 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8663 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8664 x, y, element, element_info[element].token_name);
8665 printf("HandleElementChange(): This should never happen!\n");
8670 /* this can happen with classic bombs on walkable, changing elements */
8671 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8674 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8675 ChangeDelay[x][y] = 0;
8681 if (ChangeDelay[x][y] == 0) /* initialize element change */
8683 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8685 if (change->can_change)
8687 ResetGfxAnimation(x, y);
8688 ResetRandomAnimationValue(x, y);
8690 if (change->pre_change_function)
8691 change->pre_change_function(x, y);
8695 ChangeDelay[x][y]--;
8697 if (ChangeDelay[x][y] != 0) /* continue element change */
8699 if (change->can_change)
8701 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8703 if (IS_ANIMATED(graphic))
8704 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8706 if (change->change_function)
8707 change->change_function(x, y);
8710 else /* finish element change */
8712 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8714 page = ChangePage[x][y];
8715 ChangePage[x][y] = -1;
8717 change = &ei->change_page[page];
8720 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8722 ChangeDelay[x][y] = 1; /* try change after next move step */
8723 ChangePage[x][y] = page; /* remember page to use for change */
8728 if (change->can_change)
8730 if (ChangeElement(x, y, element, page))
8732 if (change->post_change_function)
8733 change->post_change_function(x, y);
8737 if (change->has_action)
8738 ExecuteCustomElementAction(x, y, element, page);
8744 static void HandleElementChange(int x, int y, int page)
8746 int element = MovingOrBlocked2Element(x, y);
8747 struct ElementInfo *ei = &element_info[element];
8748 struct ElementChangeInfo *change = &ei->change_page[page];
8751 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8754 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8755 x, y, element, element_info[element].token_name);
8756 printf("HandleElementChange(): This should never happen!\n");
8761 /* this can happen with classic bombs on walkable, changing elements */
8762 if (!CAN_CHANGE(element))
8765 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8766 ChangeDelay[x][y] = 0;
8772 if (ChangeDelay[x][y] == 0) /* initialize element change */
8774 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8776 ResetGfxAnimation(x, y);
8777 ResetRandomAnimationValue(x, y);
8779 if (change->pre_change_function)
8780 change->pre_change_function(x, y);
8783 ChangeDelay[x][y]--;
8785 if (ChangeDelay[x][y] != 0) /* continue element change */
8787 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8789 if (IS_ANIMATED(graphic))
8790 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8792 if (change->change_function)
8793 change->change_function(x, y);
8795 else /* finish element change */
8797 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8799 page = ChangePage[x][y];
8800 ChangePage[x][y] = -1;
8802 change = &ei->change_page[page];
8805 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8807 ChangeDelay[x][y] = 1; /* try change after next move step */
8808 ChangePage[x][y] = page; /* remember page to use for change */
8813 if (ChangeElement(x, y, element, page))
8815 if (change->post_change_function)
8816 change->post_change_function(x, y);
8823 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8824 int trigger_element,
8830 boolean change_done_any = FALSE;
8831 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8834 if (!(trigger_events[trigger_element][trigger_event]))
8837 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8839 int element = EL_CUSTOM_START + i;
8840 boolean change_done = FALSE;
8843 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8844 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8847 for (p = 0; p < element_info[element].num_change_pages; p++)
8849 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8851 if (change->can_change_or_has_action &&
8852 change->has_event[trigger_event] &&
8853 change->trigger_side & trigger_side &&
8854 change->trigger_player & trigger_player &&
8855 change->trigger_page & trigger_page_bits &&
8856 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8858 change->actual_trigger_element = trigger_element;
8859 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8860 change->actual_trigger_side = trigger_side;
8861 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8862 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8864 if ((change->can_change && !change_done) || change->has_action)
8869 SCAN_PLAYFIELD(x, y)
8871 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8874 if (Feld[x][y] == element)
8876 if (change->can_change && !change_done)
8878 ChangeDelay[x][y] = 1;
8879 ChangeEvent[x][y] = trigger_event;
8881 HandleElementChange(x, y, p);
8883 #if USE_NEW_DELAYED_ACTION
8884 else if (change->has_action)
8886 ExecuteCustomElementAction(x, y, element, p);
8887 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8890 if (change->has_action)
8892 ExecuteCustomElementAction(x, y, element, p);
8893 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8899 if (change->can_change)
8902 change_done_any = TRUE;
8909 return change_done_any;
8912 static boolean CheckElementChangeExt(int x, int y,
8914 int trigger_element,
8919 boolean change_done = FALSE;
8922 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8923 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8926 if (Feld[x][y] == EL_BLOCKED)
8928 Blocked2Moving(x, y, &x, &y);
8929 element = Feld[x][y];
8933 /* check if element has already changed */
8934 if (Feld[x][y] != element)
8937 /* check if element has already changed or is about to change after moving */
8938 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8939 Feld[x][y] != element) ||
8941 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8942 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8943 ChangePage[x][y] != -1)))
8947 for (p = 0; p < element_info[element].num_change_pages; p++)
8949 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8951 boolean check_trigger_element =
8952 (trigger_event == CE_TOUCHING_X ||
8953 trigger_event == CE_HITTING_X ||
8954 trigger_event == CE_HIT_BY_X);
8956 if (change->can_change_or_has_action &&
8957 change->has_event[trigger_event] &&
8958 change->trigger_side & trigger_side &&
8959 change->trigger_player & trigger_player &&
8960 (!check_trigger_element ||
8961 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8963 change->actual_trigger_element = trigger_element;
8964 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8965 change->actual_trigger_side = trigger_side;
8966 change->actual_trigger_ce_value = CustomValue[x][y];
8967 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8969 /* special case: trigger element not at (x,y) position for some events */
8970 if (check_trigger_element)
8982 { 0, 0 }, { 0, 0 }, { 0, 0 },
8986 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8987 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8989 change->actual_trigger_ce_value = CustomValue[xx][yy];
8990 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8993 if (change->can_change && !change_done)
8995 ChangeDelay[x][y] = 1;
8996 ChangeEvent[x][y] = trigger_event;
8998 HandleElementChange(x, y, p);
9002 #if USE_NEW_DELAYED_ACTION
9003 else if (change->has_action)
9005 ExecuteCustomElementAction(x, y, element, p);
9006 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9009 if (change->has_action)
9011 ExecuteCustomElementAction(x, y, element, p);
9012 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9021 static void PlayPlayerSound(struct PlayerInfo *player)
9023 int jx = player->jx, jy = player->jy;
9024 int sound_element = player->artwork_element;
9025 int last_action = player->last_action_waiting;
9026 int action = player->action_waiting;
9028 if (player->is_waiting)
9030 if (action != last_action)
9031 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9033 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9037 if (action != last_action)
9038 StopSound(element_info[sound_element].sound[last_action]);
9040 if (last_action == ACTION_SLEEPING)
9041 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9045 static void PlayAllPlayersSound()
9049 for (i = 0; i < MAX_PLAYERS; i++)
9050 if (stored_player[i].active)
9051 PlayPlayerSound(&stored_player[i]);
9054 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9056 boolean last_waiting = player->is_waiting;
9057 int move_dir = player->MovDir;
9059 player->dir_waiting = move_dir;
9060 player->last_action_waiting = player->action_waiting;
9064 if (!last_waiting) /* not waiting -> waiting */
9066 player->is_waiting = TRUE;
9068 player->frame_counter_bored =
9070 game.player_boring_delay_fixed +
9071 SimpleRND(game.player_boring_delay_random);
9072 player->frame_counter_sleeping =
9074 game.player_sleeping_delay_fixed +
9075 SimpleRND(game.player_sleeping_delay_random);
9078 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9080 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9084 if (game.player_sleeping_delay_fixed +
9085 game.player_sleeping_delay_random > 0 &&
9086 player->anim_delay_counter == 0 &&
9087 player->post_delay_counter == 0 &&
9088 FrameCounter >= player->frame_counter_sleeping)
9089 player->is_sleeping = TRUE;
9090 else if (game.player_boring_delay_fixed +
9091 game.player_boring_delay_random > 0 &&
9092 FrameCounter >= player->frame_counter_bored)
9093 player->is_bored = TRUE;
9095 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9096 player->is_bored ? ACTION_BORING :
9100 if (player->is_sleeping && player->use_murphy)
9102 /* special case for sleeping Murphy when leaning against non-free tile */
9104 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9105 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9106 !IS_MOVING(player->jx - 1, player->jy)))
9108 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9109 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9110 !IS_MOVING(player->jx + 1, player->jy)))
9111 move_dir = MV_RIGHT;
9113 player->is_sleeping = FALSE;
9115 player->dir_waiting = move_dir;
9119 if (player->is_sleeping)
9121 if (player->num_special_action_sleeping > 0)
9123 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9125 int last_special_action = player->special_action_sleeping;
9126 int num_special_action = player->num_special_action_sleeping;
9127 int special_action =
9128 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9129 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9130 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9131 last_special_action + 1 : ACTION_SLEEPING);
9132 int special_graphic =
9133 el_act_dir2img(player->artwork_element, special_action, move_dir);
9135 player->anim_delay_counter =
9136 graphic_info[special_graphic].anim_delay_fixed +
9137 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9138 player->post_delay_counter =
9139 graphic_info[special_graphic].post_delay_fixed +
9140 SimpleRND(graphic_info[special_graphic].post_delay_random);
9142 player->special_action_sleeping = special_action;
9145 if (player->anim_delay_counter > 0)
9147 player->action_waiting = player->special_action_sleeping;
9148 player->anim_delay_counter--;
9150 else if (player->post_delay_counter > 0)
9152 player->post_delay_counter--;
9156 else if (player->is_bored)
9158 if (player->num_special_action_bored > 0)
9160 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9162 int special_action =
9163 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9164 int special_graphic =
9165 el_act_dir2img(player->artwork_element, special_action, move_dir);
9167 player->anim_delay_counter =
9168 graphic_info[special_graphic].anim_delay_fixed +
9169 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9170 player->post_delay_counter =
9171 graphic_info[special_graphic].post_delay_fixed +
9172 SimpleRND(graphic_info[special_graphic].post_delay_random);
9174 player->special_action_bored = special_action;
9177 if (player->anim_delay_counter > 0)
9179 player->action_waiting = player->special_action_bored;
9180 player->anim_delay_counter--;
9182 else if (player->post_delay_counter > 0)
9184 player->post_delay_counter--;
9189 else if (last_waiting) /* waiting -> not waiting */
9191 player->is_waiting = FALSE;
9192 player->is_bored = FALSE;
9193 player->is_sleeping = FALSE;
9195 player->frame_counter_bored = -1;
9196 player->frame_counter_sleeping = -1;
9198 player->anim_delay_counter = 0;
9199 player->post_delay_counter = 0;
9201 player->dir_waiting = player->MovDir;
9202 player->action_waiting = ACTION_DEFAULT;
9204 player->special_action_bored = ACTION_DEFAULT;
9205 player->special_action_sleeping = ACTION_DEFAULT;
9209 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9211 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9212 int left = player_action & JOY_LEFT;
9213 int right = player_action & JOY_RIGHT;
9214 int up = player_action & JOY_UP;
9215 int down = player_action & JOY_DOWN;
9216 int button1 = player_action & JOY_BUTTON_1;
9217 int button2 = player_action & JOY_BUTTON_2;
9218 int dx = (left ? -1 : right ? 1 : 0);
9219 int dy = (up ? -1 : down ? 1 : 0);
9221 if (!player->active || tape.pausing)
9227 snapped = SnapField(player, dx, dy);
9231 dropped = DropElement(player);
9233 moved = MovePlayer(player, dx, dy);
9236 if (tape.single_step && tape.recording && !tape.pausing)
9238 if (button1 || (dropped && !moved))
9240 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9241 SnapField(player, 0, 0); /* stop snapping */
9245 SetPlayerWaiting(player, FALSE);
9247 return player_action;
9251 /* no actions for this player (no input at player's configured device) */
9253 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9254 SnapField(player, 0, 0);
9255 CheckGravityMovementWhenNotMoving(player);
9257 if (player->MovPos == 0)
9258 SetPlayerWaiting(player, TRUE);
9260 if (player->MovPos == 0) /* needed for tape.playing */
9261 player->is_moving = FALSE;
9263 player->is_dropping = FALSE;
9264 player->is_dropping_pressed = FALSE;
9265 player->drop_pressed_delay = 0;
9271 static void CheckLevelTime()
9275 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9277 if (level.native_em_level->lev->home == 0) /* all players at home */
9279 local_player->LevelSolved = TRUE;
9280 AllPlayersGone = TRUE;
9282 level.native_em_level->lev->home = -1;
9285 if (level.native_em_level->ply[0]->alive == 0 &&
9286 level.native_em_level->ply[1]->alive == 0 &&
9287 level.native_em_level->ply[2]->alive == 0 &&
9288 level.native_em_level->ply[3]->alive == 0) /* all dead */
9289 AllPlayersGone = TRUE;
9292 if (TimeFrames >= FRAMES_PER_SECOND)
9297 for (i = 0; i < MAX_PLAYERS; i++)
9299 struct PlayerInfo *player = &stored_player[i];
9301 if (SHIELD_ON(player))
9303 player->shield_normal_time_left--;
9305 if (player->shield_deadly_time_left > 0)
9306 player->shield_deadly_time_left--;
9310 if (!level.use_step_counter)
9318 if (TimeLeft <= 10 && setup.time_limit)
9319 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9321 DrawGameValue_Time(TimeLeft);
9323 if (!TimeLeft && setup.time_limit)
9325 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9326 level.native_em_level->lev->killed_out_of_time = TRUE;
9328 for (i = 0; i < MAX_PLAYERS; i++)
9329 KillPlayer(&stored_player[i]);
9332 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9333 DrawGameValue_Time(TimePlayed);
9335 level.native_em_level->lev->time =
9336 (level.time == 0 ? TimePlayed : TimeLeft);
9339 if (tape.recording || tape.playing)
9340 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9344 void AdvanceFrameAndPlayerCounters(int player_nr)
9349 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9350 FrameCounter, FrameCounter + 1);
9353 /* advance frame counters (global frame counter and time frame counter) */
9357 /* advance player counters (counters for move delay, move animation etc.) */
9358 for (i = 0; i < MAX_PLAYERS; i++)
9360 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9361 int move_delay_value = stored_player[i].move_delay_value;
9362 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9364 if (!advance_player_counters) /* not all players may be affected */
9367 #if USE_NEW_PLAYER_ANIM
9368 if (move_frames == 0) /* less than one move per game frame */
9370 int stepsize = TILEX / move_delay_value;
9371 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9372 int count = (stored_player[i].is_moving ?
9373 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9375 if (count % delay == 0)
9380 stored_player[i].Frame += move_frames;
9382 if (stored_player[i].MovPos != 0)
9383 stored_player[i].StepFrame += move_frames;
9385 if (stored_player[i].move_delay > 0)
9386 stored_player[i].move_delay--;
9388 /* due to bugs in previous versions, counter must count up, not down */
9389 if (stored_player[i].push_delay != -1)
9390 stored_player[i].push_delay++;
9392 if (stored_player[i].drop_delay > 0)
9393 stored_player[i].drop_delay--;
9395 if (stored_player[i].is_dropping_pressed)
9396 stored_player[i].drop_pressed_delay++;
9400 void StartGameActions(boolean init_network_game, boolean record_tape,
9403 unsigned long new_random_seed = InitRND(random_seed);
9406 TapeStartRecording(new_random_seed);
9408 #if defined(NETWORK_AVALIABLE)
9409 if (init_network_game)
9411 SendToServer_StartPlaying();
9419 game_status = GAME_MODE_PLAYING;
9426 static unsigned long game_frame_delay = 0;
9427 unsigned long game_frame_delay_value;
9428 byte *recorded_player_action;
9429 byte summarized_player_action = 0;
9430 byte tape_action[MAX_PLAYERS];
9433 if (game.restart_level)
9434 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9436 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9438 if (level.native_em_level->lev->home == 0) /* all players at home */
9440 local_player->LevelSolved = TRUE;
9441 AllPlayersGone = TRUE;
9443 level.native_em_level->lev->home = -1;
9446 if (level.native_em_level->ply[0]->alive == 0 &&
9447 level.native_em_level->ply[1]->alive == 0 &&
9448 level.native_em_level->ply[2]->alive == 0 &&
9449 level.native_em_level->ply[3]->alive == 0) /* all dead */
9450 AllPlayersGone = TRUE;
9453 if (local_player->LevelSolved)
9456 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9459 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9462 game_frame_delay_value =
9463 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9465 if (tape.playing && tape.warp_forward && !tape.pausing)
9466 game_frame_delay_value = 0;
9468 /* ---------- main game synchronization point ---------- */
9470 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9472 if (network_playing && !network_player_action_received)
9474 /* try to get network player actions in time */
9476 #if defined(NETWORK_AVALIABLE)
9477 /* last chance to get network player actions without main loop delay */
9481 /* game was quit by network peer */
9482 if (game_status != GAME_MODE_PLAYING)
9485 if (!network_player_action_received)
9486 return; /* failed to get network player actions in time */
9488 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9494 /* at this point we know that we really continue executing the game */
9497 network_player_action_received = FALSE;
9500 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9502 if (tape.set_centered_player)
9504 game.centered_player_nr_next = tape.centered_player_nr_next;
9505 game.set_centered_player = TRUE;
9508 for (i = 0; i < MAX_PLAYERS; i++)
9510 summarized_player_action |= stored_player[i].action;
9512 if (!network_playing)
9513 stored_player[i].effective_action = stored_player[i].action;
9516 #if defined(NETWORK_AVALIABLE)
9517 if (network_playing)
9518 SendToServer_MovePlayer(summarized_player_action);
9521 if (!options.network && !setup.team_mode)
9522 local_player->effective_action = summarized_player_action;
9524 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9526 for (i = 0; i < MAX_PLAYERS; i++)
9527 stored_player[i].effective_action =
9528 (i == game.centered_player_nr ? summarized_player_action : 0);
9531 if (recorded_player_action != NULL)
9532 for (i = 0; i < MAX_PLAYERS; i++)
9533 stored_player[i].effective_action = recorded_player_action[i];
9535 for (i = 0; i < MAX_PLAYERS; i++)
9537 tape_action[i] = stored_player[i].effective_action;
9539 /* (this can only happen in the R'n'D game engine) */
9540 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9541 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9544 /* only record actions from input devices, but not programmed actions */
9546 TapeRecordAction(tape_action);
9548 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9550 GameActions_EM_Main();
9558 void GameActions_EM_Main()
9560 byte effective_action[MAX_PLAYERS];
9561 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9564 for (i = 0; i < MAX_PLAYERS; i++)
9565 effective_action[i] = stored_player[i].effective_action;
9567 GameActions_EM(effective_action, warp_mode);
9571 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9574 void GameActions_RND()
9576 int magic_wall_x = 0, magic_wall_y = 0;
9577 int i, x, y, element, graphic;
9579 InitPlayfieldScanModeVars();
9581 #if USE_ONE_MORE_CHANGE_PER_FRAME
9582 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9584 SCAN_PLAYFIELD(x, y)
9586 ChangeCount[x][y] = 0;
9587 ChangeEvent[x][y] = -1;
9593 if (game.set_centered_player)
9595 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9597 /* switching to "all players" only possible if all players fit to screen */
9598 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9600 game.centered_player_nr_next = game.centered_player_nr;
9601 game.set_centered_player = FALSE;
9604 /* do not switch focus to non-existing (or non-active) player */
9605 if (game.centered_player_nr_next >= 0 &&
9606 !stored_player[game.centered_player_nr_next].active)
9608 game.centered_player_nr_next = game.centered_player_nr;
9609 game.set_centered_player = FALSE;
9613 if (game.set_centered_player &&
9614 ScreenMovPos == 0) /* screen currently aligned at tile position */
9618 if (game.centered_player_nr_next == -1)
9620 setScreenCenteredToAllPlayers(&sx, &sy);
9624 sx = stored_player[game.centered_player_nr_next].jx;
9625 sy = stored_player[game.centered_player_nr_next].jy;
9628 game.centered_player_nr = game.centered_player_nr_next;
9629 game.set_centered_player = FALSE;
9631 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9632 DrawGameDoorValues();
9636 for (i = 0; i < MAX_PLAYERS; i++)
9638 int actual_player_action = stored_player[i].effective_action;
9641 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9642 - rnd_equinox_tetrachloride 048
9643 - rnd_equinox_tetrachloride_ii 096
9644 - rnd_emanuel_schmieg 002
9645 - doctor_sloan_ww 001, 020
9647 if (stored_player[i].MovPos == 0)
9648 CheckGravityMovement(&stored_player[i]);
9651 /* overwrite programmed action with tape action */
9652 if (stored_player[i].programmed_action)
9653 actual_player_action = stored_player[i].programmed_action;
9656 PlayerActions(&stored_player[i], actual_player_action);
9658 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9660 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9661 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9664 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9668 network_player_action_received = FALSE;
9671 ScrollScreen(NULL, SCROLL_GO_ON);
9673 /* for backwards compatibility, the following code emulates a fixed bug that
9674 occured when pushing elements (causing elements that just made their last
9675 pushing step to already (if possible) make their first falling step in the
9676 same game frame, which is bad); this code is also needed to use the famous
9677 "spring push bug" which is used in older levels and might be wanted to be
9678 used also in newer levels, but in this case the buggy pushing code is only
9679 affecting the "spring" element and no other elements */
9681 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9683 for (i = 0; i < MAX_PLAYERS; i++)
9685 struct PlayerInfo *player = &stored_player[i];
9689 if (player->active && player->is_pushing && player->is_moving &&
9691 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9692 Feld[x][y] == EL_SPRING))
9694 ContinueMoving(x, y);
9696 /* continue moving after pushing (this is actually a bug) */
9697 if (!IS_MOVING(x, y))
9706 SCAN_PLAYFIELD(x, y)
9708 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9711 ChangeCount[x][y] = 0;
9712 ChangeEvent[x][y] = -1;
9714 /* this must be handled before main playfield loop */
9715 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9718 if (MovDelay[x][y] <= 0)
9722 #if USE_NEW_SNAP_DELAY
9723 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9726 if (MovDelay[x][y] <= 0)
9729 DrawLevelField(x, y);
9731 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9737 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9739 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9740 printf("GameActions(): This should never happen!\n");
9742 ChangePage[x][y] = -1;
9747 if (WasJustMoving[x][y] > 0)
9748 WasJustMoving[x][y]--;
9749 if (WasJustFalling[x][y] > 0)
9750 WasJustFalling[x][y]--;
9751 if (CheckCollision[x][y] > 0)
9752 CheckCollision[x][y]--;
9756 /* reset finished pushing action (not done in ContinueMoving() to allow
9757 continuous pushing animation for elements with zero push delay) */
9758 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9760 ResetGfxAnimation(x, y);
9761 DrawLevelField(x, y);
9765 if (IS_BLOCKED(x, y))
9769 Blocked2Moving(x, y, &oldx, &oldy);
9770 if (!IS_MOVING(oldx, oldy))
9772 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9773 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9774 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9775 printf("GameActions(): This should never happen!\n");
9782 SCAN_PLAYFIELD(x, y)
9784 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9787 element = Feld[x][y];
9788 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9791 printf("::: %d,%d\n", x, y);
9793 if (element == EL_ROCK)
9794 printf("::: Yo man! Rocks can fall!\n");
9798 ResetGfxFrame(x, y, TRUE);
9800 if (graphic_info[graphic].anim_global_sync)
9801 GfxFrame[x][y] = FrameCounter;
9802 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9804 int old_gfx_frame = GfxFrame[x][y];
9806 GfxFrame[x][y] = CustomValue[x][y];
9809 if (GfxFrame[x][y] != old_gfx_frame)
9811 DrawLevelGraphicAnimation(x, y, graphic);
9813 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9815 int old_gfx_frame = GfxFrame[x][y];
9817 GfxFrame[x][y] = element_info[element].collect_score;
9820 if (GfxFrame[x][y] != old_gfx_frame)
9822 DrawLevelGraphicAnimation(x, y, graphic);
9826 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9827 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9828 ResetRandomAnimationValue(x, y);
9830 SetRandomAnimationValue(x, y);
9832 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9834 if (IS_INACTIVE(element))
9836 if (IS_ANIMATED(graphic))
9837 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9842 /* this may take place after moving, so 'element' may have changed */
9843 if (IS_CHANGING(x, y) &&
9844 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9846 int page = element_info[element].event_page_nr[CE_DELAY];
9848 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9852 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9856 if (element == EL_CUSTOM_255)
9857 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9861 HandleElementChange(x, y, page);
9863 if (CAN_CHANGE(element))
9864 HandleElementChange(x, y, page);
9866 if (HAS_ACTION(element))
9867 ExecuteCustomElementAction(x, y, element, page);
9872 element = Feld[x][y];
9873 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9876 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9880 element = Feld[x][y];
9881 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9883 if (IS_ANIMATED(graphic) &&
9886 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9888 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9889 EdelsteinFunkeln(x, y);
9891 else if ((element == EL_ACID ||
9892 element == EL_EXIT_OPEN ||
9893 element == EL_SP_EXIT_OPEN ||
9894 element == EL_SP_TERMINAL ||
9895 element == EL_SP_TERMINAL_ACTIVE ||
9896 element == EL_EXTRA_TIME ||
9897 element == EL_SHIELD_NORMAL ||
9898 element == EL_SHIELD_DEADLY) &&
9899 IS_ANIMATED(graphic))
9900 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9901 else if (IS_MOVING(x, y))
9902 ContinueMoving(x, y);
9903 else if (IS_ACTIVE_BOMB(element))
9904 CheckDynamite(x, y);
9905 else if (element == EL_AMOEBA_GROWING)
9906 AmoebeWaechst(x, y);
9907 else if (element == EL_AMOEBA_SHRINKING)
9908 AmoebaDisappearing(x, y);
9910 #if !USE_NEW_AMOEBA_CODE
9911 else if (IS_AMOEBALIVE(element))
9912 AmoebeAbleger(x, y);
9915 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9917 else if (element == EL_EXIT_CLOSED)
9919 else if (element == EL_SP_EXIT_CLOSED)
9921 else if (element == EL_EXPANDABLE_WALL_GROWING)
9923 else if (element == EL_EXPANDABLE_WALL ||
9924 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9925 element == EL_EXPANDABLE_WALL_VERTICAL ||
9926 element == EL_EXPANDABLE_WALL_ANY)
9928 else if (element == EL_FLAMES)
9929 CheckForDragon(x, y);
9930 else if (element == EL_EXPLOSION)
9931 ; /* drawing of correct explosion animation is handled separately */
9932 else if (element == EL_ELEMENT_SNAPPING ||
9933 element == EL_DIAGONAL_SHRINKING ||
9934 element == EL_DIAGONAL_GROWING)
9937 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9939 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9942 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9946 if (element == EL_CUSTOM_255 ||
9947 element == EL_CUSTOM_256)
9948 DrawLevelGraphicAnimation(x, y, graphic);
9951 if (IS_BELT_ACTIVE(element))
9952 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9954 if (game.magic_wall_active)
9956 int jx = local_player->jx, jy = local_player->jy;
9958 /* play the element sound at the position nearest to the player */
9959 if ((element == EL_MAGIC_WALL_FULL ||
9960 element == EL_MAGIC_WALL_ACTIVE ||
9961 element == EL_MAGIC_WALL_EMPTYING ||
9962 element == EL_BD_MAGIC_WALL_FULL ||
9963 element == EL_BD_MAGIC_WALL_ACTIVE ||
9964 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9965 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9973 #if USE_NEW_AMOEBA_CODE
9974 /* new experimental amoeba growth stuff */
9975 if (!(FrameCounter % 8))
9977 static unsigned long random = 1684108901;
9979 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9981 x = RND(lev_fieldx);
9982 y = RND(lev_fieldy);
9983 element = Feld[x][y];
9985 if (!IS_PLAYER(x,y) &&
9986 (element == EL_EMPTY ||
9987 CAN_GROW_INTO(element) ||
9988 element == EL_QUICKSAND_EMPTY ||
9989 element == EL_ACID_SPLASH_LEFT ||
9990 element == EL_ACID_SPLASH_RIGHT))
9992 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9993 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9994 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9995 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9996 Feld[x][y] = EL_AMOEBA_DROP;
9999 random = random * 129 + 1;
10005 if (game.explosions_delayed)
10008 game.explosions_delayed = FALSE;
10011 SCAN_PLAYFIELD(x, y)
10013 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10016 element = Feld[x][y];
10018 if (ExplodeField[x][y])
10019 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10020 else if (element == EL_EXPLOSION)
10021 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10023 ExplodeField[x][y] = EX_TYPE_NONE;
10026 game.explosions_delayed = TRUE;
10029 if (game.magic_wall_active)
10031 if (!(game.magic_wall_time_left % 4))
10033 int element = Feld[magic_wall_x][magic_wall_y];
10035 if (element == EL_BD_MAGIC_WALL_FULL ||
10036 element == EL_BD_MAGIC_WALL_ACTIVE ||
10037 element == EL_BD_MAGIC_WALL_EMPTYING)
10038 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10040 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10043 if (game.magic_wall_time_left > 0)
10045 game.magic_wall_time_left--;
10046 if (!game.magic_wall_time_left)
10049 SCAN_PLAYFIELD(x, y)
10051 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10054 element = Feld[x][y];
10056 if (element == EL_MAGIC_WALL_ACTIVE ||
10057 element == EL_MAGIC_WALL_FULL)
10059 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10060 DrawLevelField(x, y);
10062 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10063 element == EL_BD_MAGIC_WALL_FULL)
10065 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10066 DrawLevelField(x, y);
10070 game.magic_wall_active = FALSE;
10075 if (game.light_time_left > 0)
10077 game.light_time_left--;
10079 if (game.light_time_left == 0)
10080 RedrawAllLightSwitchesAndInvisibleElements();
10083 if (game.timegate_time_left > 0)
10085 game.timegate_time_left--;
10087 if (game.timegate_time_left == 0)
10088 CloseAllOpenTimegates();
10091 if (game.lenses_time_left > 0)
10093 game.lenses_time_left--;
10095 if (game.lenses_time_left == 0)
10096 RedrawAllInvisibleElementsForLenses();
10099 if (game.magnify_time_left > 0)
10101 game.magnify_time_left--;
10103 if (game.magnify_time_left == 0)
10104 RedrawAllInvisibleElementsForMagnifier();
10107 for (i = 0; i < MAX_PLAYERS; i++)
10109 struct PlayerInfo *player = &stored_player[i];
10111 if (SHIELD_ON(player))
10113 if (player->shield_deadly_time_left)
10114 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10115 else if (player->shield_normal_time_left)
10116 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10123 PlayAllPlayersSound();
10125 if (options.debug) /* calculate frames per second */
10127 static unsigned long fps_counter = 0;
10128 static int fps_frames = 0;
10129 unsigned long fps_delay_ms = Counter() - fps_counter;
10133 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10135 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10138 fps_counter = Counter();
10141 redraw_mask |= REDRAW_FPS;
10144 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10146 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10148 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10150 local_player->show_envelope = 0;
10153 /* use random number generator in every frame to make it less predictable */
10154 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10158 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10160 int min_x = x, min_y = y, max_x = x, max_y = y;
10163 for (i = 0; i < MAX_PLAYERS; i++)
10165 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10167 if (!stored_player[i].active || &stored_player[i] == player)
10170 min_x = MIN(min_x, jx);
10171 min_y = MIN(min_y, jy);
10172 max_x = MAX(max_x, jx);
10173 max_y = MAX(max_y, jy);
10176 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10179 static boolean AllPlayersInVisibleScreen()
10183 for (i = 0; i < MAX_PLAYERS; i++)
10185 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10187 if (!stored_player[i].active)
10190 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10197 void ScrollLevel(int dx, int dy)
10199 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10202 BlitBitmap(drawto_field, drawto_field,
10203 FX + TILEX * (dx == -1) - softscroll_offset,
10204 FY + TILEY * (dy == -1) - softscroll_offset,
10205 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10206 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10207 FX + TILEX * (dx == 1) - softscroll_offset,
10208 FY + TILEY * (dy == 1) - softscroll_offset);
10212 x = (dx == 1 ? BX1 : BX2);
10213 for (y = BY1; y <= BY2; y++)
10214 DrawScreenField(x, y);
10219 y = (dy == 1 ? BY1 : BY2);
10220 for (x = BX1; x <= BX2; x++)
10221 DrawScreenField(x, y);
10224 redraw_mask |= REDRAW_FIELD;
10227 static boolean canFallDown(struct PlayerInfo *player)
10229 int jx = player->jx, jy = player->jy;
10231 return (IN_LEV_FIELD(jx, jy + 1) &&
10232 (IS_FREE(jx, jy + 1) ||
10233 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10234 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10235 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10238 static boolean canPassField(int x, int y, int move_dir)
10240 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10241 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10242 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10243 int nextx = x + dx;
10244 int nexty = y + dy;
10245 int element = Feld[x][y];
10247 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10248 !CAN_MOVE(element) &&
10249 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10250 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10251 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10254 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10256 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10257 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10258 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10262 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10263 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10264 (IS_DIGGABLE(Feld[newx][newy]) ||
10265 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10266 canPassField(newx, newy, move_dir)));
10269 static void CheckGravityMovement(struct PlayerInfo *player)
10271 if (game.gravity && !player->programmed_action)
10273 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10274 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10275 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10276 int jx = player->jx, jy = player->jy;
10277 boolean player_is_moving_to_valid_field =
10278 (!player_is_snapping &&
10279 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10280 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10281 boolean player_can_fall_down = canFallDown(player);
10283 if (player_can_fall_down &&
10284 !player_is_moving_to_valid_field)
10285 player->programmed_action = MV_DOWN;
10289 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10291 return CheckGravityMovement(player);
10293 if (game.gravity && !player->programmed_action)
10295 int jx = player->jx, jy = player->jy;
10296 boolean field_under_player_is_free =
10297 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10298 boolean player_is_standing_on_valid_field =
10299 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10300 (IS_WALKABLE(Feld[jx][jy]) &&
10301 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10303 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10304 player->programmed_action = MV_DOWN;
10309 MovePlayerOneStep()
10310 -----------------------------------------------------------------------------
10311 dx, dy: direction (non-diagonal) to try to move the player to
10312 real_dx, real_dy: direction as read from input device (can be diagonal)
10315 boolean MovePlayerOneStep(struct PlayerInfo *player,
10316 int dx, int dy, int real_dx, int real_dy)
10318 int jx = player->jx, jy = player->jy;
10319 int new_jx = jx + dx, new_jy = jy + dy;
10320 #if !USE_FIXED_DONT_RUN_INTO
10324 boolean player_can_move = !player->cannot_move;
10326 if (!player->active || (!dx && !dy))
10327 return MP_NO_ACTION;
10329 player->MovDir = (dx < 0 ? MV_LEFT :
10330 dx > 0 ? MV_RIGHT :
10332 dy > 0 ? MV_DOWN : MV_NONE);
10334 if (!IN_LEV_FIELD(new_jx, new_jy))
10335 return MP_NO_ACTION;
10337 if (!player_can_move)
10340 if (player->MovPos == 0)
10342 player->is_moving = FALSE;
10343 player->is_digging = FALSE;
10344 player->is_collecting = FALSE;
10345 player->is_snapping = FALSE;
10346 player->is_pushing = FALSE;
10349 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10350 SnapField(player, 0, 0);
10354 return MP_NO_ACTION;
10359 if (!options.network && game.centered_player_nr == -1 &&
10360 !AllPlayersInSight(player, new_jx, new_jy))
10361 return MP_NO_ACTION;
10363 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10364 return MP_NO_ACTION;
10367 #if !USE_FIXED_DONT_RUN_INTO
10368 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10370 /* (moved to DigField()) */
10371 if (player_can_move && DONT_RUN_INTO(element))
10373 if (element == EL_ACID && dx == 0 && dy == 1)
10375 SplashAcid(new_jx, new_jy);
10376 Feld[jx][jy] = EL_PLAYER_1;
10377 InitMovingField(jx, jy, MV_DOWN);
10378 Store[jx][jy] = EL_ACID;
10379 ContinueMoving(jx, jy);
10380 BuryPlayer(player);
10383 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10389 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10391 #if USE_FIXED_DONT_RUN_INTO
10392 if (can_move == MP_DONT_RUN_INTO)
10396 if (can_move != MP_MOVING)
10399 #if USE_FIXED_DONT_RUN_INTO
10402 /* check if DigField() has caused relocation of the player */
10403 if (player->jx != jx || player->jy != jy)
10404 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10406 StorePlayer[jx][jy] = 0;
10407 player->last_jx = jx;
10408 player->last_jy = jy;
10409 player->jx = new_jx;
10410 player->jy = new_jy;
10411 StorePlayer[new_jx][new_jy] = player->element_nr;
10413 if (player->move_delay_value_next != -1)
10415 player->move_delay_value = player->move_delay_value_next;
10416 player->move_delay_value_next = -1;
10420 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10422 player->step_counter++;
10424 PlayerVisit[jx][jy] = FrameCounter;
10426 ScrollPlayer(player, SCROLL_INIT);
10431 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10433 int jx = player->jx, jy = player->jy;
10434 int old_jx = jx, old_jy = jy;
10435 int moved = MP_NO_ACTION;
10437 if (!player->active)
10442 if (player->MovPos == 0)
10444 player->is_moving = FALSE;
10445 player->is_digging = FALSE;
10446 player->is_collecting = FALSE;
10447 player->is_snapping = FALSE;
10448 player->is_pushing = FALSE;
10454 if (player->move_delay > 0)
10457 player->move_delay = -1; /* set to "uninitialized" value */
10459 /* store if player is automatically moved to next field */
10460 player->is_auto_moving = (player->programmed_action != MV_NONE);
10462 /* remove the last programmed player action */
10463 player->programmed_action = 0;
10465 if (player->MovPos)
10467 /* should only happen if pre-1.2 tape recordings are played */
10468 /* this is only for backward compatibility */
10470 int original_move_delay_value = player->move_delay_value;
10473 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10477 /* scroll remaining steps with finest movement resolution */
10478 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10480 while (player->MovPos)
10482 ScrollPlayer(player, SCROLL_GO_ON);
10483 ScrollScreen(NULL, SCROLL_GO_ON);
10485 AdvanceFrameAndPlayerCounters(player->index_nr);
10491 player->move_delay_value = original_move_delay_value;
10494 if (player->last_move_dir & MV_HORIZONTAL)
10496 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10497 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10501 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10502 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10509 if (moved & MP_MOVING && !ScreenMovPos &&
10510 (player->index_nr == game.centered_player_nr ||
10511 game.centered_player_nr == -1))
10513 if (moved & MP_MOVING && !ScreenMovPos &&
10514 (player == local_player || !options.network))
10517 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10518 int offset = (setup.scroll_delay ? 3 : 0);
10520 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10522 /* actual player has left the screen -- scroll in that direction */
10523 if (jx != old_jx) /* player has moved horizontally */
10524 scroll_x += (jx - old_jx);
10525 else /* player has moved vertically */
10526 scroll_y += (jy - old_jy);
10530 if (jx != old_jx) /* player has moved horizontally */
10532 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10533 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10534 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10536 /* don't scroll over playfield boundaries */
10537 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10538 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10540 /* don't scroll more than one field at a time */
10541 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10543 /* don't scroll against the player's moving direction */
10544 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10545 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10546 scroll_x = old_scroll_x;
10548 else /* player has moved vertically */
10550 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10551 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10552 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10554 /* don't scroll over playfield boundaries */
10555 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10556 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10558 /* don't scroll more than one field at a time */
10559 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10561 /* don't scroll against the player's moving direction */
10562 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10563 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10564 scroll_y = old_scroll_y;
10568 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10571 if (!options.network && game.centered_player_nr == -1 &&
10572 !AllPlayersInVisibleScreen())
10574 scroll_x = old_scroll_x;
10575 scroll_y = old_scroll_y;
10579 if (!options.network && !AllPlayersInVisibleScreen())
10581 scroll_x = old_scroll_x;
10582 scroll_y = old_scroll_y;
10587 ScrollScreen(player, SCROLL_INIT);
10588 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10593 player->StepFrame = 0;
10595 if (moved & MP_MOVING)
10597 if (old_jx != jx && old_jy == jy)
10598 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10599 else if (old_jx == jx && old_jy != jy)
10600 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10602 DrawLevelField(jx, jy); /* for "crumbled sand" */
10604 player->last_move_dir = player->MovDir;
10605 player->is_moving = TRUE;
10606 player->is_snapping = FALSE;
10607 player->is_switching = FALSE;
10608 player->is_dropping = FALSE;
10609 player->is_dropping_pressed = FALSE;
10610 player->drop_pressed_delay = 0;
10614 CheckGravityMovementWhenNotMoving(player);
10616 player->is_moving = FALSE;
10618 /* at this point, the player is allowed to move, but cannot move right now
10619 (e.g. because of something blocking the way) -- ensure that the player
10620 is also allowed to move in the next frame (in old versions before 3.1.1,
10621 the player was forced to wait again for eight frames before next try) */
10623 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10624 player->move_delay = 0; /* allow direct movement in the next frame */
10627 if (player->move_delay == -1) /* not yet initialized by DigField() */
10628 player->move_delay = player->move_delay_value;
10630 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10632 TestIfPlayerTouchesBadThing(jx, jy);
10633 TestIfPlayerTouchesCustomElement(jx, jy);
10636 if (!player->active)
10637 RemovePlayer(player);
10642 void ScrollPlayer(struct PlayerInfo *player, int mode)
10644 int jx = player->jx, jy = player->jy;
10645 int last_jx = player->last_jx, last_jy = player->last_jy;
10646 int move_stepsize = TILEX / player->move_delay_value;
10648 #if USE_NEW_PLAYER_SPEED
10649 if (!player->active)
10652 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10655 if (!player->active || player->MovPos == 0)
10659 if (mode == SCROLL_INIT)
10661 player->actual_frame_counter = FrameCounter;
10662 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10664 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10665 Feld[last_jx][last_jy] == EL_EMPTY)
10667 int last_field_block_delay = 0; /* start with no blocking at all */
10668 int block_delay_adjustment = player->block_delay_adjustment;
10670 /* if player blocks last field, add delay for exactly one move */
10671 if (player->block_last_field)
10673 last_field_block_delay += player->move_delay_value;
10675 /* when blocking enabled, prevent moving up despite gravity */
10676 if (game.gravity && player->MovDir == MV_UP)
10677 block_delay_adjustment = -1;
10680 /* add block delay adjustment (also possible when not blocking) */
10681 last_field_block_delay += block_delay_adjustment;
10683 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10684 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10687 #if USE_NEW_PLAYER_SPEED
10688 if (player->MovPos != 0) /* player has not yet reached destination */
10694 else if (!FrameReached(&player->actual_frame_counter, 1))
10698 printf("::: player->MovPos: %d -> %d\n",
10700 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10703 #if USE_NEW_PLAYER_SPEED
10704 if (player->MovPos != 0)
10706 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10707 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10709 /* before DrawPlayer() to draw correct player graphic for this case */
10710 if (player->MovPos == 0)
10711 CheckGravityMovement(player);
10714 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10715 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10717 /* before DrawPlayer() to draw correct player graphic for this case */
10718 if (player->MovPos == 0)
10719 CheckGravityMovement(player);
10722 if (player->MovPos == 0) /* player reached destination field */
10725 printf("::: player reached destination field\n");
10728 if (player->move_delay_reset_counter > 0)
10730 player->move_delay_reset_counter--;
10732 if (player->move_delay_reset_counter == 0)
10734 /* continue with normal speed after quickly moving through gate */
10735 HALVE_PLAYER_SPEED(player);
10737 /* be able to make the next move without delay */
10738 player->move_delay = 0;
10742 player->last_jx = jx;
10743 player->last_jy = jy;
10745 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10746 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10747 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10749 DrawPlayer(player); /* needed here only to cleanup last field */
10750 RemovePlayer(player);
10752 if (local_player->friends_still_needed == 0 ||
10753 IS_SP_ELEMENT(Feld[jx][jy]))
10754 player->LevelSolved = player->GameOver = TRUE;
10757 /* this breaks one level: "machine", level 000 */
10759 int move_direction = player->MovDir;
10760 int enter_side = MV_DIR_OPPOSITE(move_direction);
10761 int leave_side = move_direction;
10762 int old_jx = last_jx;
10763 int old_jy = last_jy;
10764 int old_element = Feld[old_jx][old_jy];
10765 int new_element = Feld[jx][jy];
10767 if (IS_CUSTOM_ELEMENT(old_element))
10768 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10770 player->index_bit, leave_side);
10772 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10773 CE_PLAYER_LEAVES_X,
10774 player->index_bit, leave_side);
10776 if (IS_CUSTOM_ELEMENT(new_element))
10777 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10778 player->index_bit, enter_side);
10780 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10781 CE_PLAYER_ENTERS_X,
10782 player->index_bit, enter_side);
10784 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10785 CE_MOVE_OF_X, move_direction);
10788 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10790 TestIfPlayerTouchesBadThing(jx, jy);
10791 TestIfPlayerTouchesCustomElement(jx, jy);
10793 /* needed because pushed element has not yet reached its destination,
10794 so it would trigger a change event at its previous field location */
10795 if (!player->is_pushing)
10796 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10798 if (!player->active)
10799 RemovePlayer(player);
10802 if (level.use_step_counter)
10812 if (TimeLeft <= 10 && setup.time_limit)
10813 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10815 DrawGameValue_Time(TimeLeft);
10817 if (!TimeLeft && setup.time_limit)
10818 for (i = 0; i < MAX_PLAYERS; i++)
10819 KillPlayer(&stored_player[i]);
10821 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10822 DrawGameValue_Time(TimePlayed);
10825 if (tape.single_step && tape.recording && !tape.pausing &&
10826 !player->programmed_action)
10827 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10831 void ScrollScreen(struct PlayerInfo *player, int mode)
10833 static unsigned long screen_frame_counter = 0;
10835 if (mode == SCROLL_INIT)
10837 /* set scrolling step size according to actual player's moving speed */
10838 ScrollStepSize = TILEX / player->move_delay_value;
10840 screen_frame_counter = FrameCounter;
10841 ScreenMovDir = player->MovDir;
10842 ScreenMovPos = player->MovPos;
10843 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10846 else if (!FrameReached(&screen_frame_counter, 1))
10851 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10852 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10853 redraw_mask |= REDRAW_FIELD;
10856 ScreenMovDir = MV_NONE;
10859 void TestIfPlayerTouchesCustomElement(int x, int y)
10861 static int xy[4][2] =
10868 static int trigger_sides[4][2] =
10870 /* center side border side */
10871 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10872 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10873 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10874 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10876 static int touch_dir[4] =
10878 MV_LEFT | MV_RIGHT,
10883 int center_element = Feld[x][y]; /* should always be non-moving! */
10886 for (i = 0; i < NUM_DIRECTIONS; i++)
10888 int xx = x + xy[i][0];
10889 int yy = y + xy[i][1];
10890 int center_side = trigger_sides[i][0];
10891 int border_side = trigger_sides[i][1];
10892 int border_element;
10894 if (!IN_LEV_FIELD(xx, yy))
10897 if (IS_PLAYER(x, y))
10899 struct PlayerInfo *player = PLAYERINFO(x, y);
10901 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10902 border_element = Feld[xx][yy]; /* may be moving! */
10903 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10904 border_element = Feld[xx][yy];
10905 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10906 border_element = MovingOrBlocked2Element(xx, yy);
10908 continue; /* center and border element do not touch */
10910 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10911 player->index_bit, border_side);
10912 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10913 CE_PLAYER_TOUCHES_X,
10914 player->index_bit, border_side);
10916 else if (IS_PLAYER(xx, yy))
10918 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10920 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10922 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10923 continue; /* center and border element do not touch */
10926 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10927 player->index_bit, center_side);
10928 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10929 CE_PLAYER_TOUCHES_X,
10930 player->index_bit, center_side);
10936 #if USE_ELEMENT_TOUCHING_BUGFIX
10938 void TestIfElementTouchesCustomElement(int x, int y)
10940 static int xy[4][2] =
10947 static int trigger_sides[4][2] =
10949 /* center side border side */
10950 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10951 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10952 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10953 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10955 static int touch_dir[4] =
10957 MV_LEFT | MV_RIGHT,
10962 boolean change_center_element = FALSE;
10963 int center_element = Feld[x][y]; /* should always be non-moving! */
10964 int border_element_old[NUM_DIRECTIONS];
10967 for (i = 0; i < NUM_DIRECTIONS; i++)
10969 int xx = x + xy[i][0];
10970 int yy = y + xy[i][1];
10971 int border_element;
10973 border_element_old[i] = -1;
10975 if (!IN_LEV_FIELD(xx, yy))
10978 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10979 border_element = Feld[xx][yy]; /* may be moving! */
10980 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10981 border_element = Feld[xx][yy];
10982 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10983 border_element = MovingOrBlocked2Element(xx, yy);
10985 continue; /* center and border element do not touch */
10987 border_element_old[i] = border_element;
10990 for (i = 0; i < NUM_DIRECTIONS; i++)
10992 int xx = x + xy[i][0];
10993 int yy = y + xy[i][1];
10994 int center_side = trigger_sides[i][0];
10995 int border_element = border_element_old[i];
10997 if (border_element == -1)
11000 /* check for change of border element */
11001 CheckElementChangeBySide(xx, yy, border_element, center_element,
11002 CE_TOUCHING_X, center_side);
11005 for (i = 0; i < NUM_DIRECTIONS; i++)
11007 int border_side = trigger_sides[i][1];
11008 int border_element = border_element_old[i];
11010 if (border_element == -1)
11013 /* check for change of center element (but change it only once) */
11014 if (!change_center_element)
11015 change_center_element =
11016 CheckElementChangeBySide(x, y, center_element, border_element,
11017 CE_TOUCHING_X, border_side);
11023 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11025 static int xy[4][2] =
11032 static int trigger_sides[4][2] =
11034 /* center side border side */
11035 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11036 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11037 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11038 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11040 static int touch_dir[4] =
11042 MV_LEFT | MV_RIGHT,
11047 boolean change_center_element = FALSE;
11048 int center_element = Feld[x][y]; /* should always be non-moving! */
11051 for (i = 0; i < NUM_DIRECTIONS; i++)
11053 int xx = x + xy[i][0];
11054 int yy = y + xy[i][1];
11055 int center_side = trigger_sides[i][0];
11056 int border_side = trigger_sides[i][1];
11057 int border_element;
11059 if (!IN_LEV_FIELD(xx, yy))
11062 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11063 border_element = Feld[xx][yy]; /* may be moving! */
11064 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11065 border_element = Feld[xx][yy];
11066 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11067 border_element = MovingOrBlocked2Element(xx, yy);
11069 continue; /* center and border element do not touch */
11071 /* check for change of center element (but change it only once) */
11072 if (!change_center_element)
11073 change_center_element =
11074 CheckElementChangeBySide(x, y, center_element, border_element,
11075 CE_TOUCHING_X, border_side);
11077 /* check for change of border element */
11078 CheckElementChangeBySide(xx, yy, border_element, center_element,
11079 CE_TOUCHING_X, center_side);
11085 void TestIfElementHitsCustomElement(int x, int y, int direction)
11087 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11088 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11089 int hitx = x + dx, hity = y + dy;
11090 int hitting_element = Feld[x][y];
11091 int touched_element;
11093 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11096 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11097 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11099 if (IN_LEV_FIELD(hitx, hity))
11101 int opposite_direction = MV_DIR_OPPOSITE(direction);
11102 int hitting_side = direction;
11103 int touched_side = opposite_direction;
11104 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11105 MovDir[hitx][hity] != direction ||
11106 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11112 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11113 CE_HITTING_X, touched_side);
11115 CheckElementChangeBySide(hitx, hity, touched_element,
11116 hitting_element, CE_HIT_BY_X, hitting_side);
11118 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11119 CE_HIT_BY_SOMETHING, opposite_direction);
11123 /* "hitting something" is also true when hitting the playfield border */
11124 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11125 CE_HITTING_SOMETHING, direction);
11129 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11131 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11132 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11133 int hitx = x + dx, hity = y + dy;
11134 int hitting_element = Feld[x][y];
11135 int touched_element;
11137 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11138 !IS_FREE(hitx, hity) &&
11139 (!IS_MOVING(hitx, hity) ||
11140 MovDir[hitx][hity] != direction ||
11141 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11144 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11148 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11152 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11153 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11155 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11156 EP_CAN_SMASH_EVERYTHING, direction);
11158 if (IN_LEV_FIELD(hitx, hity))
11160 int opposite_direction = MV_DIR_OPPOSITE(direction);
11161 int hitting_side = direction;
11162 int touched_side = opposite_direction;
11164 int touched_element = MovingOrBlocked2Element(hitx, hity);
11167 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11168 MovDir[hitx][hity] != direction ||
11169 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11178 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11179 CE_SMASHED_BY_SOMETHING, opposite_direction);
11181 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11182 CE_OTHER_IS_SMASHING, touched_side);
11184 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11185 CE_OTHER_GETS_SMASHED, hitting_side);
11191 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11193 int i, kill_x = -1, kill_y = -1;
11195 int bad_element = -1;
11196 static int test_xy[4][2] =
11203 static int test_dir[4] =
11211 for (i = 0; i < NUM_DIRECTIONS; i++)
11213 int test_x, test_y, test_move_dir, test_element;
11215 test_x = good_x + test_xy[i][0];
11216 test_y = good_y + test_xy[i][1];
11218 if (!IN_LEV_FIELD(test_x, test_y))
11222 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11224 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11226 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11227 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11229 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11230 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11234 bad_element = test_element;
11240 if (kill_x != -1 || kill_y != -1)
11242 if (IS_PLAYER(good_x, good_y))
11244 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11246 if (player->shield_deadly_time_left > 0 &&
11247 !IS_INDESTRUCTIBLE(bad_element))
11248 Bang(kill_x, kill_y);
11249 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11250 KillPlayer(player);
11253 Bang(good_x, good_y);
11257 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11259 int i, kill_x = -1, kill_y = -1;
11260 int bad_element = Feld[bad_x][bad_y];
11261 static int test_xy[4][2] =
11268 static int touch_dir[4] =
11270 MV_LEFT | MV_RIGHT,
11275 static int test_dir[4] =
11283 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11286 for (i = 0; i < NUM_DIRECTIONS; i++)
11288 int test_x, test_y, test_move_dir, test_element;
11290 test_x = bad_x + test_xy[i][0];
11291 test_y = bad_y + test_xy[i][1];
11292 if (!IN_LEV_FIELD(test_x, test_y))
11296 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11298 test_element = Feld[test_x][test_y];
11300 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11301 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11303 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11304 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11306 /* good thing is player or penguin that does not move away */
11307 if (IS_PLAYER(test_x, test_y))
11309 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11311 if (bad_element == EL_ROBOT && player->is_moving)
11312 continue; /* robot does not kill player if he is moving */
11314 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11316 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11317 continue; /* center and border element do not touch */
11324 else if (test_element == EL_PENGUIN)
11333 if (kill_x != -1 || kill_y != -1)
11335 if (IS_PLAYER(kill_x, kill_y))
11337 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11339 if (player->shield_deadly_time_left > 0 &&
11340 !IS_INDESTRUCTIBLE(bad_element))
11341 Bang(bad_x, bad_y);
11342 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11343 KillPlayer(player);
11346 Bang(kill_x, kill_y);
11350 void TestIfPlayerTouchesBadThing(int x, int y)
11352 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11355 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11357 TestIfGoodThingHitsBadThing(x, y, move_dir);
11360 void TestIfBadThingTouchesPlayer(int x, int y)
11362 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11365 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11367 TestIfBadThingHitsGoodThing(x, y, move_dir);
11370 void TestIfFriendTouchesBadThing(int x, int y)
11372 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11375 void TestIfBadThingTouchesFriend(int x, int y)
11377 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11380 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11382 int i, kill_x = bad_x, kill_y = bad_y;
11383 static int xy[4][2] =
11391 for (i = 0; i < NUM_DIRECTIONS; i++)
11395 x = bad_x + xy[i][0];
11396 y = bad_y + xy[i][1];
11397 if (!IN_LEV_FIELD(x, y))
11400 element = Feld[x][y];
11401 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11402 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11410 if (kill_x != bad_x || kill_y != bad_y)
11411 Bang(bad_x, bad_y);
11414 void KillPlayer(struct PlayerInfo *player)
11416 int jx = player->jx, jy = player->jy;
11418 if (!player->active)
11421 /* remove accessible field at the player's position */
11422 Feld[jx][jy] = EL_EMPTY;
11424 /* deactivate shield (else Bang()/Explode() would not work right) */
11425 player->shield_normal_time_left = 0;
11426 player->shield_deadly_time_left = 0;
11429 BuryPlayer(player);
11432 static void KillPlayerUnlessEnemyProtected(int x, int y)
11434 if (!PLAYER_ENEMY_PROTECTED(x, y))
11435 KillPlayer(PLAYERINFO(x, y));
11438 static void KillPlayerUnlessExplosionProtected(int x, int y)
11440 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11441 KillPlayer(PLAYERINFO(x, y));
11444 void BuryPlayer(struct PlayerInfo *player)
11446 int jx = player->jx, jy = player->jy;
11448 if (!player->active)
11451 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11452 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11454 player->GameOver = TRUE;
11455 RemovePlayer(player);
11458 void RemovePlayer(struct PlayerInfo *player)
11460 int jx = player->jx, jy = player->jy;
11461 int i, found = FALSE;
11463 player->present = FALSE;
11464 player->active = FALSE;
11466 if (!ExplodeField[jx][jy])
11467 StorePlayer[jx][jy] = 0;
11469 if (player->is_moving)
11470 DrawLevelField(player->last_jx, player->last_jy);
11472 for (i = 0; i < MAX_PLAYERS; i++)
11473 if (stored_player[i].active)
11477 AllPlayersGone = TRUE;
11483 #if USE_NEW_SNAP_DELAY
11484 static void setFieldForSnapping(int x, int y, int element, int direction)
11486 struct ElementInfo *ei = &element_info[element];
11487 int direction_bit = MV_DIR_TO_BIT(direction);
11488 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11489 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11490 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11492 Feld[x][y] = EL_ELEMENT_SNAPPING;
11493 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11495 ResetGfxAnimation(x, y);
11497 GfxElement[x][y] = element;
11498 GfxAction[x][y] = action;
11499 GfxDir[x][y] = direction;
11500 GfxFrame[x][y] = -1;
11505 =============================================================================
11506 checkDiagonalPushing()
11507 -----------------------------------------------------------------------------
11508 check if diagonal input device direction results in pushing of object
11509 (by checking if the alternative direction is walkable, diggable, ...)
11510 =============================================================================
11513 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11514 int x, int y, int real_dx, int real_dy)
11516 int jx, jy, dx, dy, xx, yy;
11518 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11521 /* diagonal direction: check alternative direction */
11526 xx = jx + (dx == 0 ? real_dx : 0);
11527 yy = jy + (dy == 0 ? real_dy : 0);
11529 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11533 =============================================================================
11535 -----------------------------------------------------------------------------
11536 x, y: field next to player (non-diagonal) to try to dig to
11537 real_dx, real_dy: direction as read from input device (can be diagonal)
11538 =============================================================================
11541 int DigField(struct PlayerInfo *player,
11542 int oldx, int oldy, int x, int y,
11543 int real_dx, int real_dy, int mode)
11545 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11546 boolean player_was_pushing = player->is_pushing;
11547 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11548 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11549 int jx = oldx, jy = oldy;
11550 int dx = x - jx, dy = y - jy;
11551 int nextx = x + dx, nexty = y + dy;
11552 int move_direction = (dx == -1 ? MV_LEFT :
11553 dx == +1 ? MV_RIGHT :
11555 dy == +1 ? MV_DOWN : MV_NONE);
11556 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11557 int dig_side = MV_DIR_OPPOSITE(move_direction);
11558 int old_element = Feld[jx][jy];
11559 #if USE_FIXED_DONT_RUN_INTO
11560 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11566 if (is_player) /* function can also be called by EL_PENGUIN */
11568 if (player->MovPos == 0)
11570 player->is_digging = FALSE;
11571 player->is_collecting = FALSE;
11574 if (player->MovPos == 0) /* last pushing move finished */
11575 player->is_pushing = FALSE;
11577 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11579 player->is_switching = FALSE;
11580 player->push_delay = -1;
11582 return MP_NO_ACTION;
11586 #if !USE_FIXED_DONT_RUN_INTO
11587 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11588 return MP_NO_ACTION;
11591 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11592 old_element = Back[jx][jy];
11594 /* in case of element dropped at player position, check background */
11595 else if (Back[jx][jy] != EL_EMPTY &&
11596 game.engine_version >= VERSION_IDENT(2,2,0,0))
11597 old_element = Back[jx][jy];
11600 #if USE_FIXED_DONT_RUN_INTO
11601 if (player_can_move && DONT_RUN_INTO(element))
11603 if (element == EL_ACID && dx == 0 && dy == 1)
11606 Feld[jx][jy] = EL_PLAYER_1;
11607 InitMovingField(jx, jy, MV_DOWN);
11608 Store[jx][jy] = EL_ACID;
11609 ContinueMoving(jx, jy);
11610 BuryPlayer(player);
11613 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11615 return MP_DONT_RUN_INTO;
11621 #if USE_FIXED_DONT_RUN_INTO
11622 if (player_can_move && DONT_RUN_INTO(element))
11624 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11626 return MP_DONT_RUN_INTO;
11631 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11632 return MP_NO_ACTION; /* field has no opening in this direction */
11634 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11635 return MP_NO_ACTION; /* field has no opening in this direction */
11638 #if USE_FIXED_DONT_RUN_INTO
11639 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11642 Feld[jx][jy] = EL_PLAYER_1;
11643 InitMovingField(jx, jy, MV_DOWN);
11644 Store[jx][jy] = EL_ACID;
11645 ContinueMoving(jx, jy);
11646 BuryPlayer(player);
11648 return MP_DONT_RUN_INTO;
11654 #if USE_FIXED_DONT_RUN_INTO
11655 if (player_can_move && DONT_RUN_INTO(element))
11657 if (element == EL_ACID && dx == 0 && dy == 1)
11660 Feld[jx][jy] = EL_PLAYER_1;
11661 InitMovingField(jx, jy, MV_DOWN);
11662 Store[jx][jy] = EL_ACID;
11663 ContinueMoving(jx, jy);
11664 BuryPlayer(player);
11667 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11669 return MP_DONT_RUN_INTO;
11674 #if USE_FIXED_DONT_RUN_INTO
11675 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11676 return MP_NO_ACTION;
11679 #if !USE_FIXED_DONT_RUN_INTO
11680 element = Feld[x][y];
11683 collect_count = element_info[element].collect_count_initial;
11685 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11686 return MP_NO_ACTION;
11688 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11689 player_can_move = player_can_move_or_snap;
11691 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11692 game.engine_version >= VERSION_IDENT(2,2,0,0))
11694 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11695 player->index_bit, dig_side);
11696 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11697 player->index_bit, dig_side);
11699 if (Feld[x][y] != element) /* field changed by snapping */
11702 return MP_NO_ACTION;
11705 if (game.gravity && is_player && !player->is_auto_moving &&
11706 canFallDown(player) && move_direction != MV_DOWN &&
11707 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11708 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11710 if (player_can_move &&
11711 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11713 int sound_element = SND_ELEMENT(element);
11714 int sound_action = ACTION_WALKING;
11716 if (IS_RND_GATE(element))
11718 if (!player->key[RND_GATE_NR(element)])
11719 return MP_NO_ACTION;
11721 else if (IS_RND_GATE_GRAY(element))
11723 if (!player->key[RND_GATE_GRAY_NR(element)])
11724 return MP_NO_ACTION;
11726 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11728 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11729 return MP_NO_ACTION;
11731 else if (element == EL_EXIT_OPEN ||
11732 element == EL_SP_EXIT_OPEN ||
11733 element == EL_SP_EXIT_OPENING)
11735 sound_action = ACTION_PASSING; /* player is passing exit */
11737 else if (element == EL_EMPTY)
11739 sound_action = ACTION_MOVING; /* nothing to walk on */
11742 /* play sound from background or player, whatever is available */
11743 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11744 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11746 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11748 else if (player_can_move &&
11749 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11751 if (!ACCESS_FROM(element, opposite_direction))
11752 return MP_NO_ACTION; /* field not accessible from this direction */
11754 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11755 return MP_NO_ACTION;
11757 if (IS_EM_GATE(element))
11759 if (!player->key[EM_GATE_NR(element)])
11760 return MP_NO_ACTION;
11762 else if (IS_EM_GATE_GRAY(element))
11764 if (!player->key[EM_GATE_GRAY_NR(element)])
11765 return MP_NO_ACTION;
11767 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11769 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11770 return MP_NO_ACTION;
11772 else if (IS_SP_PORT(element))
11774 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11775 element == EL_SP_GRAVITY_PORT_RIGHT ||
11776 element == EL_SP_GRAVITY_PORT_UP ||
11777 element == EL_SP_GRAVITY_PORT_DOWN)
11778 game.gravity = !game.gravity;
11779 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11780 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11781 element == EL_SP_GRAVITY_ON_PORT_UP ||
11782 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11783 game.gravity = TRUE;
11784 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11785 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11786 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11787 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11788 game.gravity = FALSE;
11791 /* automatically move to the next field with double speed */
11792 player->programmed_action = move_direction;
11794 if (player->move_delay_reset_counter == 0)
11796 player->move_delay_reset_counter = 2; /* two double speed steps */
11798 DOUBLE_PLAYER_SPEED(player);
11801 PlayLevelSoundAction(x, y, ACTION_PASSING);
11803 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11807 if (mode != DF_SNAP)
11809 GfxElement[x][y] = GFX_ELEMENT(element);
11810 player->is_digging = TRUE;
11813 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11815 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11816 player->index_bit, dig_side);
11818 if (mode == DF_SNAP)
11820 #if USE_NEW_SNAP_DELAY
11821 if (level.block_snap_field)
11822 setFieldForSnapping(x, y, element, move_direction);
11824 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11826 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11829 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11830 player->index_bit, dig_side);
11833 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11837 if (is_player && mode != DF_SNAP)
11839 GfxElement[x][y] = element;
11840 player->is_collecting = TRUE;
11843 if (element == EL_SPEED_PILL)
11845 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11847 else if (element == EL_EXTRA_TIME && level.time > 0)
11849 TimeLeft += level.extra_time;
11850 DrawGameValue_Time(TimeLeft);
11852 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11854 player->shield_normal_time_left += level.shield_normal_time;
11855 if (element == EL_SHIELD_DEADLY)
11856 player->shield_deadly_time_left += level.shield_deadly_time;
11858 else if (element == EL_DYNAMITE ||
11859 element == EL_EM_DYNAMITE ||
11860 element == EL_SP_DISK_RED)
11862 if (player->inventory_size < MAX_INVENTORY_SIZE)
11863 player->inventory_element[player->inventory_size++] = element;
11866 DrawGameDoorValues();
11868 DrawGameValue_Dynamite(local_player->inventory_size);
11871 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11873 player->dynabomb_count++;
11874 player->dynabombs_left++;
11876 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11878 player->dynabomb_size++;
11880 else if (element == EL_DYNABOMB_INCREASE_POWER)
11882 player->dynabomb_xl = TRUE;
11884 else if (IS_KEY(element))
11886 player->key[KEY_NR(element)] = TRUE;
11889 DrawGameDoorValues();
11891 DrawGameValue_Keys(player->key);
11894 redraw_mask |= REDRAW_DOOR_1;
11896 else if (IS_ENVELOPE(element))
11898 player->show_envelope = element;
11900 else if (element == EL_EMC_LENSES)
11902 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11904 RedrawAllInvisibleElementsForLenses();
11906 else if (element == EL_EMC_MAGNIFIER)
11908 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11910 RedrawAllInvisibleElementsForMagnifier();
11912 else if (IS_DROPPABLE(element) ||
11913 IS_THROWABLE(element)) /* can be collected and dropped */
11917 if (collect_count == 0)
11918 player->inventory_infinite_element = element;
11920 for (i = 0; i < collect_count; i++)
11921 if (player->inventory_size < MAX_INVENTORY_SIZE)
11922 player->inventory_element[player->inventory_size++] = element;
11925 DrawGameDoorValues();
11927 DrawGameValue_Dynamite(local_player->inventory_size);
11930 else if (collect_count > 0)
11932 local_player->gems_still_needed -= collect_count;
11933 if (local_player->gems_still_needed < 0)
11934 local_player->gems_still_needed = 0;
11936 DrawGameValue_Emeralds(local_player->gems_still_needed);
11939 RaiseScoreElement(element);
11940 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11943 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11944 player->index_bit, dig_side);
11946 if (mode == DF_SNAP)
11948 #if USE_NEW_SNAP_DELAY
11949 if (level.block_snap_field)
11950 setFieldForSnapping(x, y, element, move_direction);
11952 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11954 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11957 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11958 player->index_bit, dig_side);
11961 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11963 if (mode == DF_SNAP && element != EL_BD_ROCK)
11964 return MP_NO_ACTION;
11966 if (CAN_FALL(element) && dy)
11967 return MP_NO_ACTION;
11969 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11970 !(element == EL_SPRING && level.use_spring_bug))
11971 return MP_NO_ACTION;
11973 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11974 ((move_direction & MV_VERTICAL &&
11975 ((element_info[element].move_pattern & MV_LEFT &&
11976 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11977 (element_info[element].move_pattern & MV_RIGHT &&
11978 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11979 (move_direction & MV_HORIZONTAL &&
11980 ((element_info[element].move_pattern & MV_UP &&
11981 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11982 (element_info[element].move_pattern & MV_DOWN &&
11983 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11984 return MP_NO_ACTION;
11986 /* do not push elements already moving away faster than player */
11987 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11988 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11989 return MP_NO_ACTION;
11991 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11993 if (player->push_delay_value == -1 || !player_was_pushing)
11994 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11996 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11998 if (player->push_delay_value == -1)
11999 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12001 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12003 if (!player->is_pushing)
12004 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12007 player->is_pushing = TRUE;
12009 if (!(IN_LEV_FIELD(nextx, nexty) &&
12010 (IS_FREE(nextx, nexty) ||
12011 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12012 IS_SB_ELEMENT(element)))))
12013 return MP_NO_ACTION;
12015 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12016 return MP_NO_ACTION;
12018 if (player->push_delay == -1) /* new pushing; restart delay */
12019 player->push_delay = 0;
12021 if (player->push_delay < player->push_delay_value &&
12022 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12023 element != EL_SPRING && element != EL_BALLOON)
12025 /* make sure that there is no move delay before next try to push */
12026 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12027 player->move_delay = 0;
12029 return MP_NO_ACTION;
12032 if (IS_SB_ELEMENT(element))
12034 if (element == EL_SOKOBAN_FIELD_FULL)
12036 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12037 local_player->sokobanfields_still_needed++;
12040 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12042 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12043 local_player->sokobanfields_still_needed--;
12046 Feld[x][y] = EL_SOKOBAN_OBJECT;
12048 if (Back[x][y] == Back[nextx][nexty])
12049 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12050 else if (Back[x][y] != 0)
12051 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12054 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12057 if (local_player->sokobanfields_still_needed == 0 &&
12058 game.emulation == EMU_SOKOBAN)
12060 player->LevelSolved = player->GameOver = TRUE;
12061 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12065 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12067 InitMovingField(x, y, move_direction);
12068 GfxAction[x][y] = ACTION_PUSHING;
12070 if (mode == DF_SNAP)
12071 ContinueMoving(x, y);
12073 MovPos[x][y] = (dx != 0 ? dx : dy);
12075 Pushed[x][y] = TRUE;
12076 Pushed[nextx][nexty] = TRUE;
12078 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12079 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12081 player->push_delay_value = -1; /* get new value later */
12083 /* check for element change _after_ element has been pushed */
12084 if (game.use_change_when_pushing_bug)
12086 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12087 player->index_bit, dig_side);
12088 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12089 player->index_bit, dig_side);
12092 else if (IS_SWITCHABLE(element))
12094 if (PLAYER_SWITCHING(player, x, y))
12096 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12097 player->index_bit, dig_side);
12102 player->is_switching = TRUE;
12103 player->switch_x = x;
12104 player->switch_y = y;
12106 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12108 if (element == EL_ROBOT_WHEEL)
12110 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12114 DrawLevelField(x, y);
12116 else if (element == EL_SP_TERMINAL)
12121 SCAN_PLAYFIELD(xx, yy)
12123 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12126 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12128 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12129 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12132 else if (IS_BELT_SWITCH(element))
12134 ToggleBeltSwitch(x, y);
12136 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12137 element == EL_SWITCHGATE_SWITCH_DOWN)
12139 ToggleSwitchgateSwitch(x, y);
12141 else if (element == EL_LIGHT_SWITCH ||
12142 element == EL_LIGHT_SWITCH_ACTIVE)
12144 ToggleLightSwitch(x, y);
12146 else if (element == EL_TIMEGATE_SWITCH)
12148 ActivateTimegateSwitch(x, y);
12150 else if (element == EL_BALLOON_SWITCH_LEFT ||
12151 element == EL_BALLOON_SWITCH_RIGHT ||
12152 element == EL_BALLOON_SWITCH_UP ||
12153 element == EL_BALLOON_SWITCH_DOWN ||
12154 element == EL_BALLOON_SWITCH_NONE ||
12155 element == EL_BALLOON_SWITCH_ANY)
12157 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12158 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12159 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12160 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12161 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12164 else if (element == EL_LAMP)
12166 Feld[x][y] = EL_LAMP_ACTIVE;
12167 local_player->lights_still_needed--;
12169 ResetGfxAnimation(x, y);
12170 DrawLevelField(x, y);
12172 else if (element == EL_TIME_ORB_FULL)
12174 Feld[x][y] = EL_TIME_ORB_EMPTY;
12176 if (level.time > 0 || level.use_time_orb_bug)
12178 TimeLeft += level.time_orb_time;
12179 DrawGameValue_Time(TimeLeft);
12182 ResetGfxAnimation(x, y);
12183 DrawLevelField(x, y);
12185 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12186 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12190 game.ball_state = !game.ball_state;
12193 SCAN_PLAYFIELD(xx, yy)
12195 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12198 int e = Feld[xx][yy];
12200 if (game.ball_state)
12202 if (e == EL_EMC_MAGIC_BALL)
12203 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12204 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12205 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12209 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12210 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12211 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12212 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12217 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12218 player->index_bit, dig_side);
12220 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12221 player->index_bit, dig_side);
12223 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12224 player->index_bit, dig_side);
12230 if (!PLAYER_SWITCHING(player, x, y))
12232 player->is_switching = TRUE;
12233 player->switch_x = x;
12234 player->switch_y = y;
12236 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12237 player->index_bit, dig_side);
12238 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12239 player->index_bit, dig_side);
12241 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12242 player->index_bit, dig_side);
12243 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12244 player->index_bit, dig_side);
12247 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12248 player->index_bit, dig_side);
12249 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12250 player->index_bit, dig_side);
12252 return MP_NO_ACTION;
12255 player->push_delay = -1;
12257 if (is_player) /* function can also be called by EL_PENGUIN */
12259 if (Feld[x][y] != element) /* really digged/collected something */
12260 player->is_collecting = !player->is_digging;
12266 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12268 int jx = player->jx, jy = player->jy;
12269 int x = jx + dx, y = jy + dy;
12270 int snap_direction = (dx == -1 ? MV_LEFT :
12271 dx == +1 ? MV_RIGHT :
12273 dy == +1 ? MV_DOWN : MV_NONE);
12274 boolean can_continue_snapping = (level.continuous_snapping &&
12275 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12277 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12280 if (!player->active || !IN_LEV_FIELD(x, y))
12288 if (player->MovPos == 0)
12289 player->is_pushing = FALSE;
12291 player->is_snapping = FALSE;
12293 if (player->MovPos == 0)
12295 player->is_moving = FALSE;
12296 player->is_digging = FALSE;
12297 player->is_collecting = FALSE;
12303 #if USE_NEW_CONTINUOUS_SNAPPING
12304 /* prevent snapping with already pressed snap key when not allowed */
12305 if (player->is_snapping && !can_continue_snapping)
12308 if (player->is_snapping)
12312 player->MovDir = snap_direction;
12314 if (player->MovPos == 0)
12316 player->is_moving = FALSE;
12317 player->is_digging = FALSE;
12318 player->is_collecting = FALSE;
12321 player->is_dropping = FALSE;
12322 player->is_dropping_pressed = FALSE;
12323 player->drop_pressed_delay = 0;
12325 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12328 player->is_snapping = TRUE;
12330 if (player->MovPos == 0)
12332 player->is_moving = FALSE;
12333 player->is_digging = FALSE;
12334 player->is_collecting = FALSE;
12337 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12338 DrawLevelField(player->last_jx, player->last_jy);
12340 DrawLevelField(x, y);
12345 boolean DropElement(struct PlayerInfo *player)
12347 int old_element, new_element;
12348 int dropx = player->jx, dropy = player->jy;
12349 int drop_direction = player->MovDir;
12350 int drop_side = drop_direction;
12351 int drop_element = (player->inventory_size > 0 ?
12352 player->inventory_element[player->inventory_size - 1] :
12353 player->inventory_infinite_element != EL_UNDEFINED ?
12354 player->inventory_infinite_element :
12355 player->dynabombs_left > 0 ?
12356 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12359 player->is_dropping_pressed = TRUE;
12361 /* do not drop an element on top of another element; when holding drop key
12362 pressed without moving, dropped element must move away before the next
12363 element can be dropped (this is especially important if the next element
12364 is dynamite, which can be placed on background for historical reasons) */
12365 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12368 if (IS_THROWABLE(drop_element))
12370 dropx += GET_DX_FROM_DIR(drop_direction);
12371 dropy += GET_DY_FROM_DIR(drop_direction);
12373 if (!IN_LEV_FIELD(dropx, dropy))
12377 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12378 new_element = drop_element; /* default: no change when dropping */
12380 /* check if player is active, not moving and ready to drop */
12381 if (!player->active || player->MovPos || player->drop_delay > 0)
12384 /* check if player has anything that can be dropped */
12385 if (new_element == EL_UNDEFINED)
12388 /* check if drop key was pressed long enough for EM style dynamite */
12389 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12392 /* check if anything can be dropped at the current position */
12393 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12396 /* collected custom elements can only be dropped on empty fields */
12397 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12400 if (old_element != EL_EMPTY)
12401 Back[dropx][dropy] = old_element; /* store old element on this field */
12403 ResetGfxAnimation(dropx, dropy);
12404 ResetRandomAnimationValue(dropx, dropy);
12406 if (player->inventory_size > 0 ||
12407 player->inventory_infinite_element != EL_UNDEFINED)
12409 if (player->inventory_size > 0)
12411 player->inventory_size--;
12414 DrawGameDoorValues();
12416 DrawGameValue_Dynamite(local_player->inventory_size);
12419 if (new_element == EL_DYNAMITE)
12420 new_element = EL_DYNAMITE_ACTIVE;
12421 else if (new_element == EL_EM_DYNAMITE)
12422 new_element = EL_EM_DYNAMITE_ACTIVE;
12423 else if (new_element == EL_SP_DISK_RED)
12424 new_element = EL_SP_DISK_RED_ACTIVE;
12427 Feld[dropx][dropy] = new_element;
12429 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12430 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12431 el2img(Feld[dropx][dropy]), 0);
12433 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12435 /* needed if previous element just changed to "empty" in the last frame */
12436 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12438 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12439 player->index_bit, drop_side);
12440 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12442 player->index_bit, drop_side);
12444 TestIfElementTouchesCustomElement(dropx, dropy);
12446 else /* player is dropping a dyna bomb */
12448 player->dynabombs_left--;
12450 Feld[dropx][dropy] = new_element;
12452 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12453 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12454 el2img(Feld[dropx][dropy]), 0);
12456 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12459 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12460 InitField_WithBug1(dropx, dropy, FALSE);
12462 new_element = Feld[dropx][dropy]; /* element might have changed */
12464 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12465 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12467 int move_direction, nextx, nexty;
12469 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12470 MovDir[dropx][dropy] = drop_direction;
12472 move_direction = MovDir[dropx][dropy];
12473 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12474 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12476 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12477 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12480 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12481 player->is_dropping = TRUE;
12483 player->drop_pressed_delay = 0;
12484 player->is_dropping_pressed = FALSE;
12486 player->drop_x = dropx;
12487 player->drop_y = dropy;
12492 /* ------------------------------------------------------------------------- */
12493 /* game sound playing functions */
12494 /* ------------------------------------------------------------------------- */
12496 static int *loop_sound_frame = NULL;
12497 static int *loop_sound_volume = NULL;
12499 void InitPlayLevelSound()
12501 int num_sounds = getSoundListSize();
12503 checked_free(loop_sound_frame);
12504 checked_free(loop_sound_volume);
12506 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12507 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12510 static void PlayLevelSound(int x, int y, int nr)
12512 int sx = SCREENX(x), sy = SCREENY(y);
12513 int volume, stereo_position;
12514 int max_distance = 8;
12515 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12517 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12518 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12521 if (!IN_LEV_FIELD(x, y) ||
12522 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12523 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12526 volume = SOUND_MAX_VOLUME;
12528 if (!IN_SCR_FIELD(sx, sy))
12530 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12531 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12533 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12536 stereo_position = (SOUND_MAX_LEFT +
12537 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12538 (SCR_FIELDX + 2 * max_distance));
12540 if (IS_LOOP_SOUND(nr))
12542 /* This assures that quieter loop sounds do not overwrite louder ones,
12543 while restarting sound volume comparison with each new game frame. */
12545 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12548 loop_sound_volume[nr] = volume;
12549 loop_sound_frame[nr] = FrameCounter;
12552 PlaySoundExt(nr, volume, stereo_position, type);
12555 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12557 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12558 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12559 y < LEVELY(BY1) ? LEVELY(BY1) :
12560 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12564 static void PlayLevelSoundAction(int x, int y, int action)
12566 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12569 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12571 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12573 if (sound_effect != SND_UNDEFINED)
12574 PlayLevelSound(x, y, sound_effect);
12577 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12580 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12582 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12583 PlayLevelSound(x, y, sound_effect);
12586 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12588 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12590 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12591 PlayLevelSound(x, y, sound_effect);
12594 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12596 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12598 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12599 StopSound(sound_effect);
12602 static void PlayLevelMusic()
12604 if (levelset.music[level_nr] != MUS_UNDEFINED)
12605 PlayMusic(levelset.music[level_nr]); /* from config file */
12607 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12610 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12612 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12617 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12621 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12625 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12629 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12633 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12637 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12641 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12644 case SAMPLE_android_clone:
12645 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12648 case SAMPLE_android_move:
12649 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12652 case SAMPLE_spring:
12653 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12657 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12661 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12664 case SAMPLE_eater_eat:
12665 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12669 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12672 case SAMPLE_collect:
12673 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12676 case SAMPLE_diamond:
12677 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12680 case SAMPLE_squash:
12681 /* !!! CHECK THIS !!! */
12683 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12685 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12689 case SAMPLE_wonderfall:
12690 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12694 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12698 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12702 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12706 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12710 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12714 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12717 case SAMPLE_wonder:
12718 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12722 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12725 case SAMPLE_exit_open:
12726 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12729 case SAMPLE_exit_leave:
12730 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12733 case SAMPLE_dynamite:
12734 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12738 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12742 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12746 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12750 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12754 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12758 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12762 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12767 void RaiseScore(int value)
12769 local_player->score += value;
12771 DrawGameValue_Score(local_player->score);
12774 void RaiseScoreElement(int element)
12779 case EL_BD_DIAMOND:
12780 case EL_EMERALD_YELLOW:
12781 case EL_EMERALD_RED:
12782 case EL_EMERALD_PURPLE:
12783 case EL_SP_INFOTRON:
12784 RaiseScore(level.score[SC_EMERALD]);
12787 RaiseScore(level.score[SC_DIAMOND]);
12790 RaiseScore(level.score[SC_CRYSTAL]);
12793 RaiseScore(level.score[SC_PEARL]);
12796 case EL_BD_BUTTERFLY:
12797 case EL_SP_ELECTRON:
12798 RaiseScore(level.score[SC_BUG]);
12801 case EL_BD_FIREFLY:
12802 case EL_SP_SNIKSNAK:
12803 RaiseScore(level.score[SC_SPACESHIP]);
12806 case EL_DARK_YAMYAM:
12807 RaiseScore(level.score[SC_YAMYAM]);
12810 RaiseScore(level.score[SC_ROBOT]);
12813 RaiseScore(level.score[SC_PACMAN]);
12816 RaiseScore(level.score[SC_NUT]);
12819 case EL_EM_DYNAMITE:
12820 case EL_SP_DISK_RED:
12821 case EL_DYNABOMB_INCREASE_NUMBER:
12822 case EL_DYNABOMB_INCREASE_SIZE:
12823 case EL_DYNABOMB_INCREASE_POWER:
12824 RaiseScore(level.score[SC_DYNAMITE]);
12826 case EL_SHIELD_NORMAL:
12827 case EL_SHIELD_DEADLY:
12828 RaiseScore(level.score[SC_SHIELD]);
12830 case EL_EXTRA_TIME:
12831 RaiseScore(level.extra_time_score);
12845 RaiseScore(level.score[SC_KEY]);
12848 RaiseScore(element_info[element].collect_score);
12853 void RequestQuitGame(boolean ask_if_really_quit)
12855 if (AllPlayersGone ||
12856 !ask_if_really_quit ||
12857 level_editor_test_game ||
12858 Request("Do you really want to quit the game ?",
12859 REQ_ASK | REQ_STAY_CLOSED))
12861 #if defined(NETWORK_AVALIABLE)
12862 if (options.network)
12863 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12867 game_status = GAME_MODE_MAIN;
12873 if (tape.playing && tape.deactivate_display)
12874 TapeDeactivateDisplayOff(TRUE);
12876 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12878 if (tape.playing && tape.deactivate_display)
12879 TapeDeactivateDisplayOn();
12884 /* ---------- new game button stuff ---------------------------------------- */
12886 /* graphic position values for game buttons */
12887 #define GAME_BUTTON_XSIZE 30
12888 #define GAME_BUTTON_YSIZE 30
12889 #define GAME_BUTTON_XPOS 5
12890 #define GAME_BUTTON_YPOS 215
12891 #define SOUND_BUTTON_XPOS 5
12892 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12894 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12895 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12896 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12897 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12898 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12899 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12906 } gamebutton_info[NUM_GAME_BUTTONS] =
12909 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12914 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12915 GAME_CTRL_ID_PAUSE,
12919 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12924 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12925 SOUND_CTRL_ID_MUSIC,
12926 "background music on/off"
12929 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12930 SOUND_CTRL_ID_LOOPS,
12931 "sound loops on/off"
12934 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12935 SOUND_CTRL_ID_SIMPLE,
12936 "normal sounds on/off"
12940 void CreateGameButtons()
12944 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12946 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12947 struct GadgetInfo *gi;
12950 unsigned long event_mask;
12951 int gd_xoffset, gd_yoffset;
12952 int gd_x1, gd_x2, gd_y1, gd_y2;
12955 gd_xoffset = gamebutton_info[i].x;
12956 gd_yoffset = gamebutton_info[i].y;
12957 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12958 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12960 if (id == GAME_CTRL_ID_STOP ||
12961 id == GAME_CTRL_ID_PAUSE ||
12962 id == GAME_CTRL_ID_PLAY)
12964 button_type = GD_TYPE_NORMAL_BUTTON;
12966 event_mask = GD_EVENT_RELEASED;
12967 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12968 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12972 button_type = GD_TYPE_CHECK_BUTTON;
12974 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12975 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12976 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12977 event_mask = GD_EVENT_PRESSED;
12978 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12979 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12982 gi = CreateGadget(GDI_CUSTOM_ID, id,
12983 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12984 GDI_X, DX + gd_xoffset,
12985 GDI_Y, DY + gd_yoffset,
12986 GDI_WIDTH, GAME_BUTTON_XSIZE,
12987 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12988 GDI_TYPE, button_type,
12989 GDI_STATE, GD_BUTTON_UNPRESSED,
12990 GDI_CHECKED, checked,
12991 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12992 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12993 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12994 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12995 GDI_EVENT_MASK, event_mask,
12996 GDI_CALLBACK_ACTION, HandleGameButtons,
13000 Error(ERR_EXIT, "cannot create gadget");
13002 game_gadget[id] = gi;
13006 void FreeGameButtons()
13010 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13011 FreeGadget(game_gadget[i]);
13014 static void MapGameButtons()
13018 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13019 MapGadget(game_gadget[i]);
13022 void UnmapGameButtons()
13026 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13027 UnmapGadget(game_gadget[i]);
13030 static void HandleGameButtons(struct GadgetInfo *gi)
13032 int id = gi->custom_id;
13034 if (game_status != GAME_MODE_PLAYING)
13039 case GAME_CTRL_ID_STOP:
13043 RequestQuitGame(TRUE);
13046 case GAME_CTRL_ID_PAUSE:
13047 if (options.network)
13049 #if defined(NETWORK_AVALIABLE)
13051 SendToServer_ContinuePlaying();
13053 SendToServer_PausePlaying();
13057 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13060 case GAME_CTRL_ID_PLAY:
13063 #if defined(NETWORK_AVALIABLE)
13064 if (options.network)
13065 SendToServer_ContinuePlaying();
13069 tape.pausing = FALSE;
13070 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13075 case SOUND_CTRL_ID_MUSIC:
13076 if (setup.sound_music)
13078 setup.sound_music = FALSE;
13081 else if (audio.music_available)
13083 setup.sound = setup.sound_music = TRUE;
13085 SetAudioMode(setup.sound);
13091 case SOUND_CTRL_ID_LOOPS:
13092 if (setup.sound_loops)
13093 setup.sound_loops = FALSE;
13094 else if (audio.loops_available)
13096 setup.sound = setup.sound_loops = TRUE;
13097 SetAudioMode(setup.sound);
13101 case SOUND_CTRL_ID_SIMPLE:
13102 if (setup.sound_simple)
13103 setup.sound_simple = FALSE;
13104 else if (audio.sound_available)
13106 setup.sound = setup.sound_simple = TRUE;
13107 SetAudioMode(setup.sound);