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 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 player changes from accessible to unaccessible
8432 (needed for special case of dropping element which then changes) */
8433 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8434 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8442 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8443 if (ELEM_IS_PLAYER(new_element))
8444 RelocatePlayer(x, y, new_element);
8447 ChangeCount[x][y]++; /* count number of changes in the same frame */
8449 TestIfBadThingTouchesPlayer(x, y);
8450 TestIfPlayerTouchesCustomElement(x, y);
8451 TestIfElementTouchesCustomElement(x, y);
8454 static void CreateField(int x, int y, int element)
8456 CreateFieldExt(x, y, element, FALSE);
8459 static void CreateElementFromChange(int x, int y, int element)
8461 element = GET_VALID_RUNTIME_ELEMENT(element);
8463 #if USE_STOP_CHANGED_ELEMENTS
8464 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8466 int old_element = Feld[x][y];
8468 /* prevent changed element from moving in same engine frame
8469 unless both old and new element can either fall or move */
8470 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8471 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8476 CreateFieldExt(x, y, element, TRUE);
8479 static boolean ChangeElement(int x, int y, int element, int page)
8481 struct ElementInfo *ei = &element_info[element];
8482 struct ElementChangeInfo *change = &ei->change_page[page];
8483 int ce_value = CustomValue[x][y];
8484 int ce_score = ei->collect_score;
8486 int old_element = Feld[x][y];
8488 /* always use default change event to prevent running into a loop */
8489 if (ChangeEvent[x][y] == -1)
8490 ChangeEvent[x][y] = CE_DELAY;
8492 if (ChangeEvent[x][y] == CE_DELAY)
8494 /* reset actual trigger element, trigger player and action element */
8495 change->actual_trigger_element = EL_EMPTY;
8496 change->actual_trigger_player = EL_PLAYER_1;
8497 change->actual_trigger_side = CH_SIDE_NONE;
8498 change->actual_trigger_ce_value = 0;
8499 change->actual_trigger_ce_score = 0;
8502 /* do not change elements more than a specified maximum number of changes */
8503 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8506 ChangeCount[x][y]++; /* count number of changes in the same frame */
8508 if (change->explode)
8515 if (change->use_target_content)
8517 boolean complete_replace = TRUE;
8518 boolean can_replace[3][3];
8521 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8524 boolean is_walkable;
8525 boolean is_diggable;
8526 boolean is_collectible;
8527 boolean is_removable;
8528 boolean is_destructible;
8529 int ex = x + xx - 1;
8530 int ey = y + yy - 1;
8531 int content_element = change->target_content.e[xx][yy];
8534 can_replace[xx][yy] = TRUE;
8536 if (ex == x && ey == y) /* do not check changing element itself */
8539 if (content_element == EL_EMPTY_SPACE)
8541 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8546 if (!IN_LEV_FIELD(ex, ey))
8548 can_replace[xx][yy] = FALSE;
8549 complete_replace = FALSE;
8556 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8557 e = MovingOrBlocked2Element(ex, ey);
8559 is_empty = (IS_FREE(ex, ey) ||
8560 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8562 is_walkable = (is_empty || IS_WALKABLE(e));
8563 is_diggable = (is_empty || IS_DIGGABLE(e));
8564 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8565 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8566 is_removable = (is_diggable || is_collectible);
8568 can_replace[xx][yy] =
8569 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8570 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8571 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8572 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8573 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8574 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8575 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8577 if (!can_replace[xx][yy])
8578 complete_replace = FALSE;
8581 if (!change->only_if_complete || complete_replace)
8583 boolean something_has_changed = FALSE;
8585 if (change->only_if_complete && change->use_random_replace &&
8586 RND(100) < change->random_percentage)
8589 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8591 int ex = x + xx - 1;
8592 int ey = y + yy - 1;
8593 int content_element;
8595 if (can_replace[xx][yy] && (!change->use_random_replace ||
8596 RND(100) < change->random_percentage))
8598 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8599 RemoveMovingField(ex, ey);
8601 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8603 content_element = change->target_content.e[xx][yy];
8604 target_element = GET_TARGET_ELEMENT(content_element, change,
8605 ce_value, ce_score);
8607 CreateElementFromChange(ex, ey, target_element);
8609 something_has_changed = TRUE;
8611 /* for symmetry reasons, freeze newly created border elements */
8612 if (ex != x || ey != y)
8613 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8617 if (something_has_changed)
8619 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8620 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8626 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8627 ce_value, ce_score);
8629 if (element == EL_DIAGONAL_GROWING ||
8630 element == EL_DIAGONAL_SHRINKING)
8632 target_element = Store[x][y];
8634 Store[x][y] = EL_EMPTY;
8637 CreateElementFromChange(x, y, target_element);
8639 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8640 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8643 /* this uses direct change before indirect change */
8644 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8649 #if USE_NEW_DELAYED_ACTION
8651 static void HandleElementChange(int x, int y, int page)
8653 int element = MovingOrBlocked2Element(x, y);
8654 struct ElementInfo *ei = &element_info[element];
8655 struct ElementChangeInfo *change = &ei->change_page[page];
8658 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8659 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8662 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8663 x, y, element, element_info[element].token_name);
8664 printf("HandleElementChange(): This should never happen!\n");
8669 /* this can happen with classic bombs on walkable, changing elements */
8670 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8673 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8674 ChangeDelay[x][y] = 0;
8680 if (ChangeDelay[x][y] == 0) /* initialize element change */
8682 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8684 if (change->can_change)
8686 ResetGfxAnimation(x, y);
8687 ResetRandomAnimationValue(x, y);
8689 if (change->pre_change_function)
8690 change->pre_change_function(x, y);
8694 ChangeDelay[x][y]--;
8696 if (ChangeDelay[x][y] != 0) /* continue element change */
8698 if (change->can_change)
8700 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8702 if (IS_ANIMATED(graphic))
8703 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8705 if (change->change_function)
8706 change->change_function(x, y);
8709 else /* finish element change */
8711 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8713 page = ChangePage[x][y];
8714 ChangePage[x][y] = -1;
8716 change = &ei->change_page[page];
8719 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8721 ChangeDelay[x][y] = 1; /* try change after next move step */
8722 ChangePage[x][y] = page; /* remember page to use for change */
8727 if (change->can_change)
8729 if (ChangeElement(x, y, element, page))
8731 if (change->post_change_function)
8732 change->post_change_function(x, y);
8736 if (change->has_action)
8737 ExecuteCustomElementAction(x, y, element, page);
8743 static void HandleElementChange(int x, int y, int page)
8745 int element = MovingOrBlocked2Element(x, y);
8746 struct ElementInfo *ei = &element_info[element];
8747 struct ElementChangeInfo *change = &ei->change_page[page];
8750 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8753 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8754 x, y, element, element_info[element].token_name);
8755 printf("HandleElementChange(): This should never happen!\n");
8760 /* this can happen with classic bombs on walkable, changing elements */
8761 if (!CAN_CHANGE(element))
8764 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8765 ChangeDelay[x][y] = 0;
8771 if (ChangeDelay[x][y] == 0) /* initialize element change */
8773 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8775 ResetGfxAnimation(x, y);
8776 ResetRandomAnimationValue(x, y);
8778 if (change->pre_change_function)
8779 change->pre_change_function(x, y);
8782 ChangeDelay[x][y]--;
8784 if (ChangeDelay[x][y] != 0) /* continue element change */
8786 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8788 if (IS_ANIMATED(graphic))
8789 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8791 if (change->change_function)
8792 change->change_function(x, y);
8794 else /* finish element change */
8796 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8798 page = ChangePage[x][y];
8799 ChangePage[x][y] = -1;
8801 change = &ei->change_page[page];
8804 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8806 ChangeDelay[x][y] = 1; /* try change after next move step */
8807 ChangePage[x][y] = page; /* remember page to use for change */
8812 if (ChangeElement(x, y, element, page))
8814 if (change->post_change_function)
8815 change->post_change_function(x, y);
8822 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8823 int trigger_element,
8829 boolean change_done_any = FALSE;
8830 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8833 if (!(trigger_events[trigger_element][trigger_event]))
8836 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8838 int element = EL_CUSTOM_START + i;
8839 boolean change_done = FALSE;
8842 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8843 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8846 for (p = 0; p < element_info[element].num_change_pages; p++)
8848 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8850 if (change->can_change_or_has_action &&
8851 change->has_event[trigger_event] &&
8852 change->trigger_side & trigger_side &&
8853 change->trigger_player & trigger_player &&
8854 change->trigger_page & trigger_page_bits &&
8855 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8857 change->actual_trigger_element = trigger_element;
8858 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8859 change->actual_trigger_side = trigger_side;
8860 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8861 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8863 if ((change->can_change && !change_done) || change->has_action)
8868 SCAN_PLAYFIELD(x, y)
8870 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8873 if (Feld[x][y] == element)
8875 if (change->can_change && !change_done)
8877 ChangeDelay[x][y] = 1;
8878 ChangeEvent[x][y] = trigger_event;
8880 HandleElementChange(x, y, p);
8882 #if USE_NEW_DELAYED_ACTION
8883 else if (change->has_action)
8885 ExecuteCustomElementAction(x, y, element, p);
8886 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8889 if (change->has_action)
8891 ExecuteCustomElementAction(x, y, element, p);
8892 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8898 if (change->can_change)
8901 change_done_any = TRUE;
8908 return change_done_any;
8911 static boolean CheckElementChangeExt(int x, int y,
8913 int trigger_element,
8918 boolean change_done = FALSE;
8921 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8922 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8925 if (Feld[x][y] == EL_BLOCKED)
8927 Blocked2Moving(x, y, &x, &y);
8928 element = Feld[x][y];
8932 /* check if element has already changed */
8933 if (Feld[x][y] != element)
8936 /* check if element has already changed or is about to change after moving */
8937 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8938 Feld[x][y] != element) ||
8940 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8941 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8942 ChangePage[x][y] != -1)))
8946 for (p = 0; p < element_info[element].num_change_pages; p++)
8948 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8950 boolean check_trigger_element =
8951 (trigger_event == CE_TOUCHING_X ||
8952 trigger_event == CE_HITTING_X ||
8953 trigger_event == CE_HIT_BY_X);
8955 if (change->can_change_or_has_action &&
8956 change->has_event[trigger_event] &&
8957 change->trigger_side & trigger_side &&
8958 change->trigger_player & trigger_player &&
8959 (!check_trigger_element ||
8960 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8962 change->actual_trigger_element = trigger_element;
8963 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8964 change->actual_trigger_side = trigger_side;
8965 change->actual_trigger_ce_value = CustomValue[x][y];
8966 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8968 /* special case: trigger element not at (x,y) position for some events */
8969 if (check_trigger_element)
8981 { 0, 0 }, { 0, 0 }, { 0, 0 },
8985 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8986 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8988 change->actual_trigger_ce_value = CustomValue[xx][yy];
8989 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8992 if (change->can_change && !change_done)
8994 ChangeDelay[x][y] = 1;
8995 ChangeEvent[x][y] = trigger_event;
8997 HandleElementChange(x, y, p);
9001 #if USE_NEW_DELAYED_ACTION
9002 else if (change->has_action)
9004 ExecuteCustomElementAction(x, y, element, p);
9005 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9008 if (change->has_action)
9010 ExecuteCustomElementAction(x, y, element, p);
9011 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9020 static void PlayPlayerSound(struct PlayerInfo *player)
9022 int jx = player->jx, jy = player->jy;
9023 int sound_element = player->artwork_element;
9024 int last_action = player->last_action_waiting;
9025 int action = player->action_waiting;
9027 if (player->is_waiting)
9029 if (action != last_action)
9030 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9032 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9036 if (action != last_action)
9037 StopSound(element_info[sound_element].sound[last_action]);
9039 if (last_action == ACTION_SLEEPING)
9040 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9044 static void PlayAllPlayersSound()
9048 for (i = 0; i < MAX_PLAYERS; i++)
9049 if (stored_player[i].active)
9050 PlayPlayerSound(&stored_player[i]);
9053 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9055 boolean last_waiting = player->is_waiting;
9056 int move_dir = player->MovDir;
9058 player->dir_waiting = move_dir;
9059 player->last_action_waiting = player->action_waiting;
9063 if (!last_waiting) /* not waiting -> waiting */
9065 player->is_waiting = TRUE;
9067 player->frame_counter_bored =
9069 game.player_boring_delay_fixed +
9070 SimpleRND(game.player_boring_delay_random);
9071 player->frame_counter_sleeping =
9073 game.player_sleeping_delay_fixed +
9074 SimpleRND(game.player_sleeping_delay_random);
9077 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9079 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9083 if (game.player_sleeping_delay_fixed +
9084 game.player_sleeping_delay_random > 0 &&
9085 player->anim_delay_counter == 0 &&
9086 player->post_delay_counter == 0 &&
9087 FrameCounter >= player->frame_counter_sleeping)
9088 player->is_sleeping = TRUE;
9089 else if (game.player_boring_delay_fixed +
9090 game.player_boring_delay_random > 0 &&
9091 FrameCounter >= player->frame_counter_bored)
9092 player->is_bored = TRUE;
9094 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9095 player->is_bored ? ACTION_BORING :
9099 if (player->is_sleeping && player->use_murphy)
9101 /* special case for sleeping Murphy when leaning against non-free tile */
9103 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9104 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9105 !IS_MOVING(player->jx - 1, player->jy)))
9107 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9108 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9109 !IS_MOVING(player->jx + 1, player->jy)))
9110 move_dir = MV_RIGHT;
9112 player->is_sleeping = FALSE;
9114 player->dir_waiting = move_dir;
9118 if (player->is_sleeping)
9120 if (player->num_special_action_sleeping > 0)
9122 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9124 int last_special_action = player->special_action_sleeping;
9125 int num_special_action = player->num_special_action_sleeping;
9126 int special_action =
9127 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9128 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9129 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9130 last_special_action + 1 : ACTION_SLEEPING);
9131 int special_graphic =
9132 el_act_dir2img(player->artwork_element, special_action, move_dir);
9134 player->anim_delay_counter =
9135 graphic_info[special_graphic].anim_delay_fixed +
9136 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9137 player->post_delay_counter =
9138 graphic_info[special_graphic].post_delay_fixed +
9139 SimpleRND(graphic_info[special_graphic].post_delay_random);
9141 player->special_action_sleeping = special_action;
9144 if (player->anim_delay_counter > 0)
9146 player->action_waiting = player->special_action_sleeping;
9147 player->anim_delay_counter--;
9149 else if (player->post_delay_counter > 0)
9151 player->post_delay_counter--;
9155 else if (player->is_bored)
9157 if (player->num_special_action_bored > 0)
9159 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9161 int special_action =
9162 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9163 int special_graphic =
9164 el_act_dir2img(player->artwork_element, special_action, move_dir);
9166 player->anim_delay_counter =
9167 graphic_info[special_graphic].anim_delay_fixed +
9168 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9169 player->post_delay_counter =
9170 graphic_info[special_graphic].post_delay_fixed +
9171 SimpleRND(graphic_info[special_graphic].post_delay_random);
9173 player->special_action_bored = special_action;
9176 if (player->anim_delay_counter > 0)
9178 player->action_waiting = player->special_action_bored;
9179 player->anim_delay_counter--;
9181 else if (player->post_delay_counter > 0)
9183 player->post_delay_counter--;
9188 else if (last_waiting) /* waiting -> not waiting */
9190 player->is_waiting = FALSE;
9191 player->is_bored = FALSE;
9192 player->is_sleeping = FALSE;
9194 player->frame_counter_bored = -1;
9195 player->frame_counter_sleeping = -1;
9197 player->anim_delay_counter = 0;
9198 player->post_delay_counter = 0;
9200 player->dir_waiting = player->MovDir;
9201 player->action_waiting = ACTION_DEFAULT;
9203 player->special_action_bored = ACTION_DEFAULT;
9204 player->special_action_sleeping = ACTION_DEFAULT;
9208 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9210 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9211 int left = player_action & JOY_LEFT;
9212 int right = player_action & JOY_RIGHT;
9213 int up = player_action & JOY_UP;
9214 int down = player_action & JOY_DOWN;
9215 int button1 = player_action & JOY_BUTTON_1;
9216 int button2 = player_action & JOY_BUTTON_2;
9217 int dx = (left ? -1 : right ? 1 : 0);
9218 int dy = (up ? -1 : down ? 1 : 0);
9220 if (!player->active || tape.pausing)
9226 snapped = SnapField(player, dx, dy);
9230 dropped = DropElement(player);
9232 moved = MovePlayer(player, dx, dy);
9235 if (tape.single_step && tape.recording && !tape.pausing)
9237 if (button1 || (dropped && !moved))
9239 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9240 SnapField(player, 0, 0); /* stop snapping */
9244 SetPlayerWaiting(player, FALSE);
9246 return player_action;
9250 /* no actions for this player (no input at player's configured device) */
9252 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9253 SnapField(player, 0, 0);
9254 CheckGravityMovementWhenNotMoving(player);
9256 if (player->MovPos == 0)
9257 SetPlayerWaiting(player, TRUE);
9259 if (player->MovPos == 0) /* needed for tape.playing */
9260 player->is_moving = FALSE;
9262 player->is_dropping = FALSE;
9263 player->is_dropping_pressed = FALSE;
9264 player->drop_pressed_delay = 0;
9270 static void CheckLevelTime()
9274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9276 if (level.native_em_level->lev->home == 0) /* all players at home */
9278 local_player->LevelSolved = TRUE;
9279 AllPlayersGone = TRUE;
9281 level.native_em_level->lev->home = -1;
9284 if (level.native_em_level->ply[0]->alive == 0 &&
9285 level.native_em_level->ply[1]->alive == 0 &&
9286 level.native_em_level->ply[2]->alive == 0 &&
9287 level.native_em_level->ply[3]->alive == 0) /* all dead */
9288 AllPlayersGone = TRUE;
9291 if (TimeFrames >= FRAMES_PER_SECOND)
9296 for (i = 0; i < MAX_PLAYERS; i++)
9298 struct PlayerInfo *player = &stored_player[i];
9300 if (SHIELD_ON(player))
9302 player->shield_normal_time_left--;
9304 if (player->shield_deadly_time_left > 0)
9305 player->shield_deadly_time_left--;
9309 if (!level.use_step_counter)
9317 if (TimeLeft <= 10 && setup.time_limit)
9318 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9320 DrawGameValue_Time(TimeLeft);
9322 if (!TimeLeft && setup.time_limit)
9324 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9325 level.native_em_level->lev->killed_out_of_time = TRUE;
9327 for (i = 0; i < MAX_PLAYERS; i++)
9328 KillPlayer(&stored_player[i]);
9331 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9332 DrawGameValue_Time(TimePlayed);
9334 level.native_em_level->lev->time =
9335 (level.time == 0 ? TimePlayed : TimeLeft);
9338 if (tape.recording || tape.playing)
9339 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9343 void AdvanceFrameAndPlayerCounters(int player_nr)
9348 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9349 FrameCounter, FrameCounter + 1);
9352 /* advance frame counters (global frame counter and time frame counter) */
9356 /* advance player counters (counters for move delay, move animation etc.) */
9357 for (i = 0; i < MAX_PLAYERS; i++)
9359 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9360 int move_delay_value = stored_player[i].move_delay_value;
9361 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9363 if (!advance_player_counters) /* not all players may be affected */
9366 #if USE_NEW_PLAYER_ANIM
9367 if (move_frames == 0) /* less than one move per game frame */
9369 int stepsize = TILEX / move_delay_value;
9370 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9371 int count = (stored_player[i].is_moving ?
9372 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9374 if (count % delay == 0)
9379 stored_player[i].Frame += move_frames;
9381 if (stored_player[i].MovPos != 0)
9382 stored_player[i].StepFrame += move_frames;
9384 if (stored_player[i].move_delay > 0)
9385 stored_player[i].move_delay--;
9387 /* due to bugs in previous versions, counter must count up, not down */
9388 if (stored_player[i].push_delay != -1)
9389 stored_player[i].push_delay++;
9391 if (stored_player[i].drop_delay > 0)
9392 stored_player[i].drop_delay--;
9394 if (stored_player[i].is_dropping_pressed)
9395 stored_player[i].drop_pressed_delay++;
9399 void StartGameActions(boolean init_network_game, boolean record_tape,
9402 unsigned long new_random_seed = InitRND(random_seed);
9405 TapeStartRecording(new_random_seed);
9407 #if defined(NETWORK_AVALIABLE)
9408 if (init_network_game)
9410 SendToServer_StartPlaying();
9418 game_status = GAME_MODE_PLAYING;
9425 static unsigned long game_frame_delay = 0;
9426 unsigned long game_frame_delay_value;
9427 byte *recorded_player_action;
9428 byte summarized_player_action = 0;
9429 byte tape_action[MAX_PLAYERS];
9432 if (game.restart_level)
9433 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9435 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9437 if (level.native_em_level->lev->home == 0) /* all players at home */
9439 local_player->LevelSolved = TRUE;
9440 AllPlayersGone = TRUE;
9442 level.native_em_level->lev->home = -1;
9445 if (level.native_em_level->ply[0]->alive == 0 &&
9446 level.native_em_level->ply[1]->alive == 0 &&
9447 level.native_em_level->ply[2]->alive == 0 &&
9448 level.native_em_level->ply[3]->alive == 0) /* all dead */
9449 AllPlayersGone = TRUE;
9452 if (local_player->LevelSolved)
9455 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9458 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9461 game_frame_delay_value =
9462 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9464 if (tape.playing && tape.warp_forward && !tape.pausing)
9465 game_frame_delay_value = 0;
9467 /* ---------- main game synchronization point ---------- */
9469 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9471 if (network_playing && !network_player_action_received)
9473 /* try to get network player actions in time */
9475 #if defined(NETWORK_AVALIABLE)
9476 /* last chance to get network player actions without main loop delay */
9480 /* game was quit by network peer */
9481 if (game_status != GAME_MODE_PLAYING)
9484 if (!network_player_action_received)
9485 return; /* failed to get network player actions in time */
9487 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9493 /* at this point we know that we really continue executing the game */
9496 network_player_action_received = FALSE;
9499 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9501 if (tape.set_centered_player)
9503 game.centered_player_nr_next = tape.centered_player_nr_next;
9504 game.set_centered_player = TRUE;
9507 for (i = 0; i < MAX_PLAYERS; i++)
9509 summarized_player_action |= stored_player[i].action;
9511 if (!network_playing)
9512 stored_player[i].effective_action = stored_player[i].action;
9515 #if defined(NETWORK_AVALIABLE)
9516 if (network_playing)
9517 SendToServer_MovePlayer(summarized_player_action);
9520 if (!options.network && !setup.team_mode)
9521 local_player->effective_action = summarized_player_action;
9523 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9525 for (i = 0; i < MAX_PLAYERS; i++)
9526 stored_player[i].effective_action =
9527 (i == game.centered_player_nr ? summarized_player_action : 0);
9530 if (recorded_player_action != NULL)
9531 for (i = 0; i < MAX_PLAYERS; i++)
9532 stored_player[i].effective_action = recorded_player_action[i];
9534 for (i = 0; i < MAX_PLAYERS; i++)
9536 tape_action[i] = stored_player[i].effective_action;
9538 /* (this can only happen in the R'n'D game engine) */
9539 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9540 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9543 /* only record actions from input devices, but not programmed actions */
9545 TapeRecordAction(tape_action);
9547 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9549 GameActions_EM_Main();
9557 void GameActions_EM_Main()
9559 byte effective_action[MAX_PLAYERS];
9560 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9563 for (i = 0; i < MAX_PLAYERS; i++)
9564 effective_action[i] = stored_player[i].effective_action;
9566 GameActions_EM(effective_action, warp_mode);
9570 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9573 void GameActions_RND()
9575 int magic_wall_x = 0, magic_wall_y = 0;
9576 int i, x, y, element, graphic;
9578 InitPlayfieldScanModeVars();
9580 #if USE_ONE_MORE_CHANGE_PER_FRAME
9581 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9583 SCAN_PLAYFIELD(x, y)
9585 ChangeCount[x][y] = 0;
9586 ChangeEvent[x][y] = -1;
9592 if (game.set_centered_player)
9594 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9596 /* switching to "all players" only possible if all players fit to screen */
9597 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9599 game.centered_player_nr_next = game.centered_player_nr;
9600 game.set_centered_player = FALSE;
9603 /* do not switch focus to non-existing (or non-active) player */
9604 if (game.centered_player_nr_next >= 0 &&
9605 !stored_player[game.centered_player_nr_next].active)
9607 game.centered_player_nr_next = game.centered_player_nr;
9608 game.set_centered_player = FALSE;
9612 if (game.set_centered_player &&
9613 ScreenMovPos == 0) /* screen currently aligned at tile position */
9617 if (game.centered_player_nr_next == -1)
9619 setScreenCenteredToAllPlayers(&sx, &sy);
9623 sx = stored_player[game.centered_player_nr_next].jx;
9624 sy = stored_player[game.centered_player_nr_next].jy;
9627 game.centered_player_nr = game.centered_player_nr_next;
9628 game.set_centered_player = FALSE;
9630 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9631 DrawGameDoorValues();
9635 for (i = 0; i < MAX_PLAYERS; i++)
9637 int actual_player_action = stored_player[i].effective_action;
9640 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9641 - rnd_equinox_tetrachloride 048
9642 - rnd_equinox_tetrachloride_ii 096
9643 - rnd_emanuel_schmieg 002
9644 - doctor_sloan_ww 001, 020
9646 if (stored_player[i].MovPos == 0)
9647 CheckGravityMovement(&stored_player[i]);
9650 /* overwrite programmed action with tape action */
9651 if (stored_player[i].programmed_action)
9652 actual_player_action = stored_player[i].programmed_action;
9655 PlayerActions(&stored_player[i], actual_player_action);
9657 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9659 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9660 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9663 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9667 network_player_action_received = FALSE;
9670 ScrollScreen(NULL, SCROLL_GO_ON);
9672 /* for backwards compatibility, the following code emulates a fixed bug that
9673 occured when pushing elements (causing elements that just made their last
9674 pushing step to already (if possible) make their first falling step in the
9675 same game frame, which is bad); this code is also needed to use the famous
9676 "spring push bug" which is used in older levels and might be wanted to be
9677 used also in newer levels, but in this case the buggy pushing code is only
9678 affecting the "spring" element and no other elements */
9680 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9682 for (i = 0; i < MAX_PLAYERS; i++)
9684 struct PlayerInfo *player = &stored_player[i];
9688 if (player->active && player->is_pushing && player->is_moving &&
9690 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9691 Feld[x][y] == EL_SPRING))
9693 ContinueMoving(x, y);
9695 /* continue moving after pushing (this is actually a bug) */
9696 if (!IS_MOVING(x, y))
9705 SCAN_PLAYFIELD(x, y)
9707 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9710 ChangeCount[x][y] = 0;
9711 ChangeEvent[x][y] = -1;
9713 /* this must be handled before main playfield loop */
9714 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9717 if (MovDelay[x][y] <= 0)
9721 #if USE_NEW_SNAP_DELAY
9722 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9725 if (MovDelay[x][y] <= 0)
9728 DrawLevelField(x, y);
9730 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9736 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9738 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9739 printf("GameActions(): This should never happen!\n");
9741 ChangePage[x][y] = -1;
9746 if (WasJustMoving[x][y] > 0)
9747 WasJustMoving[x][y]--;
9748 if (WasJustFalling[x][y] > 0)
9749 WasJustFalling[x][y]--;
9750 if (CheckCollision[x][y] > 0)
9751 CheckCollision[x][y]--;
9755 /* reset finished pushing action (not done in ContinueMoving() to allow
9756 continuous pushing animation for elements with zero push delay) */
9757 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9759 ResetGfxAnimation(x, y);
9760 DrawLevelField(x, y);
9764 if (IS_BLOCKED(x, y))
9768 Blocked2Moving(x, y, &oldx, &oldy);
9769 if (!IS_MOVING(oldx, oldy))
9771 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9772 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9773 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9774 printf("GameActions(): This should never happen!\n");
9781 SCAN_PLAYFIELD(x, y)
9783 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9786 element = Feld[x][y];
9787 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9790 printf("::: %d,%d\n", x, y);
9792 if (element == EL_ROCK)
9793 printf("::: Yo man! Rocks can fall!\n");
9797 ResetGfxFrame(x, y, TRUE);
9799 if (graphic_info[graphic].anim_global_sync)
9800 GfxFrame[x][y] = FrameCounter;
9801 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9803 int old_gfx_frame = GfxFrame[x][y];
9805 GfxFrame[x][y] = CustomValue[x][y];
9808 if (GfxFrame[x][y] != old_gfx_frame)
9810 DrawLevelGraphicAnimation(x, y, graphic);
9812 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9814 int old_gfx_frame = GfxFrame[x][y];
9816 GfxFrame[x][y] = element_info[element].collect_score;
9819 if (GfxFrame[x][y] != old_gfx_frame)
9821 DrawLevelGraphicAnimation(x, y, graphic);
9825 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9826 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9827 ResetRandomAnimationValue(x, y);
9829 SetRandomAnimationValue(x, y);
9831 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9833 if (IS_INACTIVE(element))
9835 if (IS_ANIMATED(graphic))
9836 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9841 /* this may take place after moving, so 'element' may have changed */
9842 if (IS_CHANGING(x, y) &&
9843 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9845 int page = element_info[element].event_page_nr[CE_DELAY];
9847 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9851 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9855 if (element == EL_CUSTOM_255)
9856 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9860 HandleElementChange(x, y, page);
9862 if (CAN_CHANGE(element))
9863 HandleElementChange(x, y, page);
9865 if (HAS_ACTION(element))
9866 ExecuteCustomElementAction(x, y, element, page);
9871 element = Feld[x][y];
9872 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9875 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9879 element = Feld[x][y];
9880 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9882 if (IS_ANIMATED(graphic) &&
9885 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9887 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9888 EdelsteinFunkeln(x, y);
9890 else if ((element == EL_ACID ||
9891 element == EL_EXIT_OPEN ||
9892 element == EL_SP_EXIT_OPEN ||
9893 element == EL_SP_TERMINAL ||
9894 element == EL_SP_TERMINAL_ACTIVE ||
9895 element == EL_EXTRA_TIME ||
9896 element == EL_SHIELD_NORMAL ||
9897 element == EL_SHIELD_DEADLY) &&
9898 IS_ANIMATED(graphic))
9899 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9900 else if (IS_MOVING(x, y))
9901 ContinueMoving(x, y);
9902 else if (IS_ACTIVE_BOMB(element))
9903 CheckDynamite(x, y);
9904 else if (element == EL_AMOEBA_GROWING)
9905 AmoebeWaechst(x, y);
9906 else if (element == EL_AMOEBA_SHRINKING)
9907 AmoebaDisappearing(x, y);
9909 #if !USE_NEW_AMOEBA_CODE
9910 else if (IS_AMOEBALIVE(element))
9911 AmoebeAbleger(x, y);
9914 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9916 else if (element == EL_EXIT_CLOSED)
9918 else if (element == EL_SP_EXIT_CLOSED)
9920 else if (element == EL_EXPANDABLE_WALL_GROWING)
9922 else if (element == EL_EXPANDABLE_WALL ||
9923 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9924 element == EL_EXPANDABLE_WALL_VERTICAL ||
9925 element == EL_EXPANDABLE_WALL_ANY)
9927 else if (element == EL_FLAMES)
9928 CheckForDragon(x, y);
9929 else if (element == EL_EXPLOSION)
9930 ; /* drawing of correct explosion animation is handled separately */
9931 else if (element == EL_ELEMENT_SNAPPING ||
9932 element == EL_DIAGONAL_SHRINKING ||
9933 element == EL_DIAGONAL_GROWING)
9936 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9938 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9941 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9942 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9945 if (element == EL_CUSTOM_255 ||
9946 element == EL_CUSTOM_256)
9947 DrawLevelGraphicAnimation(x, y, graphic);
9950 if (IS_BELT_ACTIVE(element))
9951 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9953 if (game.magic_wall_active)
9955 int jx = local_player->jx, jy = local_player->jy;
9957 /* play the element sound at the position nearest to the player */
9958 if ((element == EL_MAGIC_WALL_FULL ||
9959 element == EL_MAGIC_WALL_ACTIVE ||
9960 element == EL_MAGIC_WALL_EMPTYING ||
9961 element == EL_BD_MAGIC_WALL_FULL ||
9962 element == EL_BD_MAGIC_WALL_ACTIVE ||
9963 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9964 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9972 #if USE_NEW_AMOEBA_CODE
9973 /* new experimental amoeba growth stuff */
9974 if (!(FrameCounter % 8))
9976 static unsigned long random = 1684108901;
9978 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9980 x = RND(lev_fieldx);
9981 y = RND(lev_fieldy);
9982 element = Feld[x][y];
9984 if (!IS_PLAYER(x,y) &&
9985 (element == EL_EMPTY ||
9986 CAN_GROW_INTO(element) ||
9987 element == EL_QUICKSAND_EMPTY ||
9988 element == EL_ACID_SPLASH_LEFT ||
9989 element == EL_ACID_SPLASH_RIGHT))
9991 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9992 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9993 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9994 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9995 Feld[x][y] = EL_AMOEBA_DROP;
9998 random = random * 129 + 1;
10004 if (game.explosions_delayed)
10007 game.explosions_delayed = FALSE;
10010 SCAN_PLAYFIELD(x, y)
10012 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10015 element = Feld[x][y];
10017 if (ExplodeField[x][y])
10018 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10019 else if (element == EL_EXPLOSION)
10020 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10022 ExplodeField[x][y] = EX_TYPE_NONE;
10025 game.explosions_delayed = TRUE;
10028 if (game.magic_wall_active)
10030 if (!(game.magic_wall_time_left % 4))
10032 int element = Feld[magic_wall_x][magic_wall_y];
10034 if (element == EL_BD_MAGIC_WALL_FULL ||
10035 element == EL_BD_MAGIC_WALL_ACTIVE ||
10036 element == EL_BD_MAGIC_WALL_EMPTYING)
10037 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10039 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10042 if (game.magic_wall_time_left > 0)
10044 game.magic_wall_time_left--;
10045 if (!game.magic_wall_time_left)
10048 SCAN_PLAYFIELD(x, y)
10050 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10053 element = Feld[x][y];
10055 if (element == EL_MAGIC_WALL_ACTIVE ||
10056 element == EL_MAGIC_WALL_FULL)
10058 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10059 DrawLevelField(x, y);
10061 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10062 element == EL_BD_MAGIC_WALL_FULL)
10064 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10065 DrawLevelField(x, y);
10069 game.magic_wall_active = FALSE;
10074 if (game.light_time_left > 0)
10076 game.light_time_left--;
10078 if (game.light_time_left == 0)
10079 RedrawAllLightSwitchesAndInvisibleElements();
10082 if (game.timegate_time_left > 0)
10084 game.timegate_time_left--;
10086 if (game.timegate_time_left == 0)
10087 CloseAllOpenTimegates();
10090 if (game.lenses_time_left > 0)
10092 game.lenses_time_left--;
10094 if (game.lenses_time_left == 0)
10095 RedrawAllInvisibleElementsForLenses();
10098 if (game.magnify_time_left > 0)
10100 game.magnify_time_left--;
10102 if (game.magnify_time_left == 0)
10103 RedrawAllInvisibleElementsForMagnifier();
10106 for (i = 0; i < MAX_PLAYERS; i++)
10108 struct PlayerInfo *player = &stored_player[i];
10110 if (SHIELD_ON(player))
10112 if (player->shield_deadly_time_left)
10113 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10114 else if (player->shield_normal_time_left)
10115 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10122 PlayAllPlayersSound();
10124 if (options.debug) /* calculate frames per second */
10126 static unsigned long fps_counter = 0;
10127 static int fps_frames = 0;
10128 unsigned long fps_delay_ms = Counter() - fps_counter;
10132 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10134 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10137 fps_counter = Counter();
10140 redraw_mask |= REDRAW_FPS;
10143 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10145 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10147 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10149 local_player->show_envelope = 0;
10152 /* use random number generator in every frame to make it less predictable */
10153 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10157 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10159 int min_x = x, min_y = y, max_x = x, max_y = y;
10162 for (i = 0; i < MAX_PLAYERS; i++)
10164 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10166 if (!stored_player[i].active || &stored_player[i] == player)
10169 min_x = MIN(min_x, jx);
10170 min_y = MIN(min_y, jy);
10171 max_x = MAX(max_x, jx);
10172 max_y = MAX(max_y, jy);
10175 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10178 static boolean AllPlayersInVisibleScreen()
10182 for (i = 0; i < MAX_PLAYERS; i++)
10184 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10186 if (!stored_player[i].active)
10189 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10196 void ScrollLevel(int dx, int dy)
10198 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10201 BlitBitmap(drawto_field, drawto_field,
10202 FX + TILEX * (dx == -1) - softscroll_offset,
10203 FY + TILEY * (dy == -1) - softscroll_offset,
10204 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10205 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10206 FX + TILEX * (dx == 1) - softscroll_offset,
10207 FY + TILEY * (dy == 1) - softscroll_offset);
10211 x = (dx == 1 ? BX1 : BX2);
10212 for (y = BY1; y <= BY2; y++)
10213 DrawScreenField(x, y);
10218 y = (dy == 1 ? BY1 : BY2);
10219 for (x = BX1; x <= BX2; x++)
10220 DrawScreenField(x, y);
10223 redraw_mask |= REDRAW_FIELD;
10226 static boolean canFallDown(struct PlayerInfo *player)
10228 int jx = player->jx, jy = player->jy;
10230 return (IN_LEV_FIELD(jx, jy + 1) &&
10231 (IS_FREE(jx, jy + 1) ||
10232 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10233 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10234 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10237 static boolean canPassField(int x, int y, int move_dir)
10239 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10240 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10241 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10242 int nextx = x + dx;
10243 int nexty = y + dy;
10244 int element = Feld[x][y];
10246 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10247 !CAN_MOVE(element) &&
10248 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10249 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10250 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10253 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10255 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10256 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10257 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10261 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10262 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10263 (IS_DIGGABLE(Feld[newx][newy]) ||
10264 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10265 canPassField(newx, newy, move_dir)));
10268 static void CheckGravityMovement(struct PlayerInfo *player)
10270 if (game.gravity && !player->programmed_action)
10272 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10273 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10274 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10275 int jx = player->jx, jy = player->jy;
10276 boolean player_is_moving_to_valid_field =
10277 (!player_is_snapping &&
10278 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10279 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10280 boolean player_can_fall_down = canFallDown(player);
10282 if (player_can_fall_down &&
10283 !player_is_moving_to_valid_field)
10284 player->programmed_action = MV_DOWN;
10288 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10290 return CheckGravityMovement(player);
10292 if (game.gravity && !player->programmed_action)
10294 int jx = player->jx, jy = player->jy;
10295 boolean field_under_player_is_free =
10296 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10297 boolean player_is_standing_on_valid_field =
10298 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10299 (IS_WALKABLE(Feld[jx][jy]) &&
10300 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10302 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10303 player->programmed_action = MV_DOWN;
10308 MovePlayerOneStep()
10309 -----------------------------------------------------------------------------
10310 dx, dy: direction (non-diagonal) to try to move the player to
10311 real_dx, real_dy: direction as read from input device (can be diagonal)
10314 boolean MovePlayerOneStep(struct PlayerInfo *player,
10315 int dx, int dy, int real_dx, int real_dy)
10317 int jx = player->jx, jy = player->jy;
10318 int new_jx = jx + dx, new_jy = jy + dy;
10319 #if !USE_FIXED_DONT_RUN_INTO
10323 boolean player_can_move = !player->cannot_move;
10325 if (!player->active || (!dx && !dy))
10326 return MP_NO_ACTION;
10328 player->MovDir = (dx < 0 ? MV_LEFT :
10329 dx > 0 ? MV_RIGHT :
10331 dy > 0 ? MV_DOWN : MV_NONE);
10333 if (!IN_LEV_FIELD(new_jx, new_jy))
10334 return MP_NO_ACTION;
10336 if (!player_can_move)
10339 if (player->MovPos == 0)
10341 player->is_moving = FALSE;
10342 player->is_digging = FALSE;
10343 player->is_collecting = FALSE;
10344 player->is_snapping = FALSE;
10345 player->is_pushing = FALSE;
10348 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10349 SnapField(player, 0, 0);
10353 return MP_NO_ACTION;
10358 if (!options.network && game.centered_player_nr == -1 &&
10359 !AllPlayersInSight(player, new_jx, new_jy))
10360 return MP_NO_ACTION;
10362 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10363 return MP_NO_ACTION;
10366 #if !USE_FIXED_DONT_RUN_INTO
10367 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10369 /* (moved to DigField()) */
10370 if (player_can_move && DONT_RUN_INTO(element))
10372 if (element == EL_ACID && dx == 0 && dy == 1)
10374 SplashAcid(new_jx, new_jy);
10375 Feld[jx][jy] = EL_PLAYER_1;
10376 InitMovingField(jx, jy, MV_DOWN);
10377 Store[jx][jy] = EL_ACID;
10378 ContinueMoving(jx, jy);
10379 BuryPlayer(player);
10382 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10388 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10390 #if USE_FIXED_DONT_RUN_INTO
10391 if (can_move == MP_DONT_RUN_INTO)
10395 if (can_move != MP_MOVING)
10398 #if USE_FIXED_DONT_RUN_INTO
10401 /* check if DigField() has caused relocation of the player */
10402 if (player->jx != jx || player->jy != jy)
10403 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10405 StorePlayer[jx][jy] = 0;
10406 player->last_jx = jx;
10407 player->last_jy = jy;
10408 player->jx = new_jx;
10409 player->jy = new_jy;
10410 StorePlayer[new_jx][new_jy] = player->element_nr;
10412 if (player->move_delay_value_next != -1)
10414 player->move_delay_value = player->move_delay_value_next;
10415 player->move_delay_value_next = -1;
10419 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10421 player->step_counter++;
10423 PlayerVisit[jx][jy] = FrameCounter;
10425 ScrollPlayer(player, SCROLL_INIT);
10430 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10432 int jx = player->jx, jy = player->jy;
10433 int old_jx = jx, old_jy = jy;
10434 int moved = MP_NO_ACTION;
10436 if (!player->active)
10441 if (player->MovPos == 0)
10443 player->is_moving = FALSE;
10444 player->is_digging = FALSE;
10445 player->is_collecting = FALSE;
10446 player->is_snapping = FALSE;
10447 player->is_pushing = FALSE;
10453 if (player->move_delay > 0)
10456 player->move_delay = -1; /* set to "uninitialized" value */
10458 /* store if player is automatically moved to next field */
10459 player->is_auto_moving = (player->programmed_action != MV_NONE);
10461 /* remove the last programmed player action */
10462 player->programmed_action = 0;
10464 if (player->MovPos)
10466 /* should only happen if pre-1.2 tape recordings are played */
10467 /* this is only for backward compatibility */
10469 int original_move_delay_value = player->move_delay_value;
10472 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10476 /* scroll remaining steps with finest movement resolution */
10477 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10479 while (player->MovPos)
10481 ScrollPlayer(player, SCROLL_GO_ON);
10482 ScrollScreen(NULL, SCROLL_GO_ON);
10484 AdvanceFrameAndPlayerCounters(player->index_nr);
10490 player->move_delay_value = original_move_delay_value;
10493 if (player->last_move_dir & MV_HORIZONTAL)
10495 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10496 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10500 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10501 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10508 if (moved & MP_MOVING && !ScreenMovPos &&
10509 (player->index_nr == game.centered_player_nr ||
10510 game.centered_player_nr == -1))
10512 if (moved & MP_MOVING && !ScreenMovPos &&
10513 (player == local_player || !options.network))
10516 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10517 int offset = (setup.scroll_delay ? 3 : 0);
10519 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10521 /* actual player has left the screen -- scroll in that direction */
10522 if (jx != old_jx) /* player has moved horizontally */
10523 scroll_x += (jx - old_jx);
10524 else /* player has moved vertically */
10525 scroll_y += (jy - old_jy);
10529 if (jx != old_jx) /* player has moved horizontally */
10531 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10532 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10533 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10535 /* don't scroll over playfield boundaries */
10536 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10537 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10539 /* don't scroll more than one field at a time */
10540 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10542 /* don't scroll against the player's moving direction */
10543 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10544 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10545 scroll_x = old_scroll_x;
10547 else /* player has moved vertically */
10549 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10550 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10551 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10553 /* don't scroll over playfield boundaries */
10554 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10555 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10557 /* don't scroll more than one field at a time */
10558 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10560 /* don't scroll against the player's moving direction */
10561 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10562 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10563 scroll_y = old_scroll_y;
10567 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10570 if (!options.network && game.centered_player_nr == -1 &&
10571 !AllPlayersInVisibleScreen())
10573 scroll_x = old_scroll_x;
10574 scroll_y = old_scroll_y;
10578 if (!options.network && !AllPlayersInVisibleScreen())
10580 scroll_x = old_scroll_x;
10581 scroll_y = old_scroll_y;
10586 ScrollScreen(player, SCROLL_INIT);
10587 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10592 player->StepFrame = 0;
10594 if (moved & MP_MOVING)
10596 if (old_jx != jx && old_jy == jy)
10597 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10598 else if (old_jx == jx && old_jy != jy)
10599 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10601 DrawLevelField(jx, jy); /* for "crumbled sand" */
10603 player->last_move_dir = player->MovDir;
10604 player->is_moving = TRUE;
10605 player->is_snapping = FALSE;
10606 player->is_switching = FALSE;
10607 player->is_dropping = FALSE;
10608 player->is_dropping_pressed = FALSE;
10609 player->drop_pressed_delay = 0;
10613 CheckGravityMovementWhenNotMoving(player);
10615 player->is_moving = FALSE;
10617 /* at this point, the player is allowed to move, but cannot move right now
10618 (e.g. because of something blocking the way) -- ensure that the player
10619 is also allowed to move in the next frame (in old versions before 3.1.1,
10620 the player was forced to wait again for eight frames before next try) */
10622 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10623 player->move_delay = 0; /* allow direct movement in the next frame */
10626 if (player->move_delay == -1) /* not yet initialized by DigField() */
10627 player->move_delay = player->move_delay_value;
10629 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10631 TestIfPlayerTouchesBadThing(jx, jy);
10632 TestIfPlayerTouchesCustomElement(jx, jy);
10635 if (!player->active)
10636 RemovePlayer(player);
10641 void ScrollPlayer(struct PlayerInfo *player, int mode)
10643 int jx = player->jx, jy = player->jy;
10644 int last_jx = player->last_jx, last_jy = player->last_jy;
10645 int move_stepsize = TILEX / player->move_delay_value;
10647 #if USE_NEW_PLAYER_SPEED
10648 if (!player->active)
10651 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10654 if (!player->active || player->MovPos == 0)
10658 if (mode == SCROLL_INIT)
10660 player->actual_frame_counter = FrameCounter;
10661 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10663 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10664 Feld[last_jx][last_jy] == EL_EMPTY)
10666 int last_field_block_delay = 0; /* start with no blocking at all */
10667 int block_delay_adjustment = player->block_delay_adjustment;
10669 /* if player blocks last field, add delay for exactly one move */
10670 if (player->block_last_field)
10672 last_field_block_delay += player->move_delay_value;
10674 /* when blocking enabled, prevent moving up despite gravity */
10675 if (game.gravity && player->MovDir == MV_UP)
10676 block_delay_adjustment = -1;
10679 /* add block delay adjustment (also possible when not blocking) */
10680 last_field_block_delay += block_delay_adjustment;
10682 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10683 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10686 #if USE_NEW_PLAYER_SPEED
10687 if (player->MovPos != 0) /* player has not yet reached destination */
10693 else if (!FrameReached(&player->actual_frame_counter, 1))
10697 printf("::: player->MovPos: %d -> %d\n",
10699 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10702 #if USE_NEW_PLAYER_SPEED
10703 if (player->MovPos != 0)
10705 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10706 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10708 /* before DrawPlayer() to draw correct player graphic for this case */
10709 if (player->MovPos == 0)
10710 CheckGravityMovement(player);
10713 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10714 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10716 /* before DrawPlayer() to draw correct player graphic for this case */
10717 if (player->MovPos == 0)
10718 CheckGravityMovement(player);
10721 if (player->MovPos == 0) /* player reached destination field */
10724 printf("::: player reached destination field\n");
10727 if (player->move_delay_reset_counter > 0)
10729 player->move_delay_reset_counter--;
10731 if (player->move_delay_reset_counter == 0)
10733 /* continue with normal speed after quickly moving through gate */
10734 HALVE_PLAYER_SPEED(player);
10736 /* be able to make the next move without delay */
10737 player->move_delay = 0;
10741 player->last_jx = jx;
10742 player->last_jy = jy;
10744 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10745 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10746 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10748 DrawPlayer(player); /* needed here only to cleanup last field */
10749 RemovePlayer(player);
10751 if (local_player->friends_still_needed == 0 ||
10752 IS_SP_ELEMENT(Feld[jx][jy]))
10753 player->LevelSolved = player->GameOver = TRUE;
10756 /* this breaks one level: "machine", level 000 */
10758 int move_direction = player->MovDir;
10759 int enter_side = MV_DIR_OPPOSITE(move_direction);
10760 int leave_side = move_direction;
10761 int old_jx = last_jx;
10762 int old_jy = last_jy;
10763 int old_element = Feld[old_jx][old_jy];
10764 int new_element = Feld[jx][jy];
10766 if (IS_CUSTOM_ELEMENT(old_element))
10767 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10769 player->index_bit, leave_side);
10771 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10772 CE_PLAYER_LEAVES_X,
10773 player->index_bit, leave_side);
10775 if (IS_CUSTOM_ELEMENT(new_element))
10776 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10777 player->index_bit, enter_side);
10779 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10780 CE_PLAYER_ENTERS_X,
10781 player->index_bit, enter_side);
10783 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10784 CE_MOVE_OF_X, move_direction);
10787 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10789 TestIfPlayerTouchesBadThing(jx, jy);
10790 TestIfPlayerTouchesCustomElement(jx, jy);
10792 /* needed because pushed element has not yet reached its destination,
10793 so it would trigger a change event at its previous field location */
10794 if (!player->is_pushing)
10795 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10797 if (!player->active)
10798 RemovePlayer(player);
10801 if (level.use_step_counter)
10811 if (TimeLeft <= 10 && setup.time_limit)
10812 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10814 DrawGameValue_Time(TimeLeft);
10816 if (!TimeLeft && setup.time_limit)
10817 for (i = 0; i < MAX_PLAYERS; i++)
10818 KillPlayer(&stored_player[i]);
10820 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10821 DrawGameValue_Time(TimePlayed);
10824 if (tape.single_step && tape.recording && !tape.pausing &&
10825 !player->programmed_action)
10826 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10830 void ScrollScreen(struct PlayerInfo *player, int mode)
10832 static unsigned long screen_frame_counter = 0;
10834 if (mode == SCROLL_INIT)
10836 /* set scrolling step size according to actual player's moving speed */
10837 ScrollStepSize = TILEX / player->move_delay_value;
10839 screen_frame_counter = FrameCounter;
10840 ScreenMovDir = player->MovDir;
10841 ScreenMovPos = player->MovPos;
10842 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10845 else if (!FrameReached(&screen_frame_counter, 1))
10850 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10851 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10852 redraw_mask |= REDRAW_FIELD;
10855 ScreenMovDir = MV_NONE;
10858 void TestIfPlayerTouchesCustomElement(int x, int y)
10860 static int xy[4][2] =
10867 static int trigger_sides[4][2] =
10869 /* center side border side */
10870 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10871 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10872 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10873 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10875 static int touch_dir[4] =
10877 MV_LEFT | MV_RIGHT,
10882 int center_element = Feld[x][y]; /* should always be non-moving! */
10885 for (i = 0; i < NUM_DIRECTIONS; i++)
10887 int xx = x + xy[i][0];
10888 int yy = y + xy[i][1];
10889 int center_side = trigger_sides[i][0];
10890 int border_side = trigger_sides[i][1];
10891 int border_element;
10893 if (!IN_LEV_FIELD(xx, yy))
10896 if (IS_PLAYER(x, y))
10898 struct PlayerInfo *player = PLAYERINFO(x, y);
10900 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10901 border_element = Feld[xx][yy]; /* may be moving! */
10902 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10903 border_element = Feld[xx][yy];
10904 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10905 border_element = MovingOrBlocked2Element(xx, yy);
10907 continue; /* center and border element do not touch */
10909 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10910 player->index_bit, border_side);
10911 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10912 CE_PLAYER_TOUCHES_X,
10913 player->index_bit, border_side);
10915 else if (IS_PLAYER(xx, yy))
10917 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10919 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10921 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10922 continue; /* center and border element do not touch */
10925 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10926 player->index_bit, center_side);
10927 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10928 CE_PLAYER_TOUCHES_X,
10929 player->index_bit, center_side);
10935 #if USE_ELEMENT_TOUCHING_BUGFIX
10937 void TestIfElementTouchesCustomElement(int x, int y)
10939 static int xy[4][2] =
10946 static int trigger_sides[4][2] =
10948 /* center side border side */
10949 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10950 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10951 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10952 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10954 static int touch_dir[4] =
10956 MV_LEFT | MV_RIGHT,
10961 boolean change_center_element = FALSE;
10962 int center_element = Feld[x][y]; /* should always be non-moving! */
10963 int border_element_old[NUM_DIRECTIONS];
10966 for (i = 0; i < NUM_DIRECTIONS; i++)
10968 int xx = x + xy[i][0];
10969 int yy = y + xy[i][1];
10970 int border_element;
10972 border_element_old[i] = -1;
10974 if (!IN_LEV_FIELD(xx, yy))
10977 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10978 border_element = Feld[xx][yy]; /* may be moving! */
10979 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10980 border_element = Feld[xx][yy];
10981 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10982 border_element = MovingOrBlocked2Element(xx, yy);
10984 continue; /* center and border element do not touch */
10986 border_element_old[i] = border_element;
10989 for (i = 0; i < NUM_DIRECTIONS; i++)
10991 int xx = x + xy[i][0];
10992 int yy = y + xy[i][1];
10993 int center_side = trigger_sides[i][0];
10994 int border_element = border_element_old[i];
10996 if (border_element == -1)
10999 /* check for change of border element */
11000 CheckElementChangeBySide(xx, yy, border_element, center_element,
11001 CE_TOUCHING_X, center_side);
11004 for (i = 0; i < NUM_DIRECTIONS; i++)
11006 int border_side = trigger_sides[i][1];
11007 int border_element = border_element_old[i];
11009 if (border_element == -1)
11012 /* check for change of center element (but change it only once) */
11013 if (!change_center_element)
11014 change_center_element =
11015 CheckElementChangeBySide(x, y, center_element, border_element,
11016 CE_TOUCHING_X, border_side);
11022 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11024 static int xy[4][2] =
11031 static int trigger_sides[4][2] =
11033 /* center side border side */
11034 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11035 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11036 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11037 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11039 static int touch_dir[4] =
11041 MV_LEFT | MV_RIGHT,
11046 boolean change_center_element = FALSE;
11047 int center_element = Feld[x][y]; /* should always be non-moving! */
11050 for (i = 0; i < NUM_DIRECTIONS; i++)
11052 int xx = x + xy[i][0];
11053 int yy = y + xy[i][1];
11054 int center_side = trigger_sides[i][0];
11055 int border_side = trigger_sides[i][1];
11056 int border_element;
11058 if (!IN_LEV_FIELD(xx, yy))
11061 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11062 border_element = Feld[xx][yy]; /* may be moving! */
11063 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11064 border_element = Feld[xx][yy];
11065 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11066 border_element = MovingOrBlocked2Element(xx, yy);
11068 continue; /* center and border element do not touch */
11070 /* check for change of center element (but change it only once) */
11071 if (!change_center_element)
11072 change_center_element =
11073 CheckElementChangeBySide(x, y, center_element, border_element,
11074 CE_TOUCHING_X, border_side);
11076 /* check for change of border element */
11077 CheckElementChangeBySide(xx, yy, border_element, center_element,
11078 CE_TOUCHING_X, center_side);
11084 void TestIfElementHitsCustomElement(int x, int y, int direction)
11086 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11087 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11088 int hitx = x + dx, hity = y + dy;
11089 int hitting_element = Feld[x][y];
11090 int touched_element;
11092 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11095 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11096 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11098 if (IN_LEV_FIELD(hitx, hity))
11100 int opposite_direction = MV_DIR_OPPOSITE(direction);
11101 int hitting_side = direction;
11102 int touched_side = opposite_direction;
11103 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11104 MovDir[hitx][hity] != direction ||
11105 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11111 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11112 CE_HITTING_X, touched_side);
11114 CheckElementChangeBySide(hitx, hity, touched_element,
11115 hitting_element, CE_HIT_BY_X, hitting_side);
11117 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11118 CE_HIT_BY_SOMETHING, opposite_direction);
11122 /* "hitting something" is also true when hitting the playfield border */
11123 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11124 CE_HITTING_SOMETHING, direction);
11128 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11130 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11131 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11132 int hitx = x + dx, hity = y + dy;
11133 int hitting_element = Feld[x][y];
11134 int touched_element;
11136 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11137 !IS_FREE(hitx, hity) &&
11138 (!IS_MOVING(hitx, hity) ||
11139 MovDir[hitx][hity] != direction ||
11140 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11143 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11147 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11151 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11152 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11154 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11155 EP_CAN_SMASH_EVERYTHING, direction);
11157 if (IN_LEV_FIELD(hitx, hity))
11159 int opposite_direction = MV_DIR_OPPOSITE(direction);
11160 int hitting_side = direction;
11161 int touched_side = opposite_direction;
11163 int touched_element = MovingOrBlocked2Element(hitx, hity);
11166 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11167 MovDir[hitx][hity] != direction ||
11168 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11177 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11178 CE_SMASHED_BY_SOMETHING, opposite_direction);
11180 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11181 CE_OTHER_IS_SMASHING, touched_side);
11183 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11184 CE_OTHER_GETS_SMASHED, hitting_side);
11190 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11192 int i, kill_x = -1, kill_y = -1;
11194 int bad_element = -1;
11195 static int test_xy[4][2] =
11202 static int test_dir[4] =
11210 for (i = 0; i < NUM_DIRECTIONS; i++)
11212 int test_x, test_y, test_move_dir, test_element;
11214 test_x = good_x + test_xy[i][0];
11215 test_y = good_y + test_xy[i][1];
11217 if (!IN_LEV_FIELD(test_x, test_y))
11221 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11223 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11225 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11226 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11228 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11229 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11233 bad_element = test_element;
11239 if (kill_x != -1 || kill_y != -1)
11241 if (IS_PLAYER(good_x, good_y))
11243 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11245 if (player->shield_deadly_time_left > 0 &&
11246 !IS_INDESTRUCTIBLE(bad_element))
11247 Bang(kill_x, kill_y);
11248 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11249 KillPlayer(player);
11252 Bang(good_x, good_y);
11256 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11258 int i, kill_x = -1, kill_y = -1;
11259 int bad_element = Feld[bad_x][bad_y];
11260 static int test_xy[4][2] =
11267 static int touch_dir[4] =
11269 MV_LEFT | MV_RIGHT,
11274 static int test_dir[4] =
11282 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11285 for (i = 0; i < NUM_DIRECTIONS; i++)
11287 int test_x, test_y, test_move_dir, test_element;
11289 test_x = bad_x + test_xy[i][0];
11290 test_y = bad_y + test_xy[i][1];
11291 if (!IN_LEV_FIELD(test_x, test_y))
11295 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11297 test_element = Feld[test_x][test_y];
11299 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11300 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11302 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11303 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11305 /* good thing is player or penguin that does not move away */
11306 if (IS_PLAYER(test_x, test_y))
11308 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11310 if (bad_element == EL_ROBOT && player->is_moving)
11311 continue; /* robot does not kill player if he is moving */
11313 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11315 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11316 continue; /* center and border element do not touch */
11323 else if (test_element == EL_PENGUIN)
11332 if (kill_x != -1 || kill_y != -1)
11334 if (IS_PLAYER(kill_x, kill_y))
11336 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11338 if (player->shield_deadly_time_left > 0 &&
11339 !IS_INDESTRUCTIBLE(bad_element))
11340 Bang(bad_x, bad_y);
11341 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11342 KillPlayer(player);
11345 Bang(kill_x, kill_y);
11349 void TestIfPlayerTouchesBadThing(int x, int y)
11351 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11354 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11356 TestIfGoodThingHitsBadThing(x, y, move_dir);
11359 void TestIfBadThingTouchesPlayer(int x, int y)
11361 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11364 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11366 TestIfBadThingHitsGoodThing(x, y, move_dir);
11369 void TestIfFriendTouchesBadThing(int x, int y)
11371 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11374 void TestIfBadThingTouchesFriend(int x, int y)
11376 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11379 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11381 int i, kill_x = bad_x, kill_y = bad_y;
11382 static int xy[4][2] =
11390 for (i = 0; i < NUM_DIRECTIONS; i++)
11394 x = bad_x + xy[i][0];
11395 y = bad_y + xy[i][1];
11396 if (!IN_LEV_FIELD(x, y))
11399 element = Feld[x][y];
11400 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11401 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11409 if (kill_x != bad_x || kill_y != bad_y)
11410 Bang(bad_x, bad_y);
11413 void KillPlayer(struct PlayerInfo *player)
11415 int jx = player->jx, jy = player->jy;
11417 if (!player->active)
11420 /* remove accessible field at the player's position */
11421 Feld[jx][jy] = EL_EMPTY;
11423 /* deactivate shield (else Bang()/Explode() would not work right) */
11424 player->shield_normal_time_left = 0;
11425 player->shield_deadly_time_left = 0;
11428 BuryPlayer(player);
11431 static void KillPlayerUnlessEnemyProtected(int x, int y)
11433 if (!PLAYER_ENEMY_PROTECTED(x, y))
11434 KillPlayer(PLAYERINFO(x, y));
11437 static void KillPlayerUnlessExplosionProtected(int x, int y)
11439 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11440 KillPlayer(PLAYERINFO(x, y));
11443 void BuryPlayer(struct PlayerInfo *player)
11445 int jx = player->jx, jy = player->jy;
11447 if (!player->active)
11450 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11451 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11453 player->GameOver = TRUE;
11454 RemovePlayer(player);
11457 void RemovePlayer(struct PlayerInfo *player)
11459 int jx = player->jx, jy = player->jy;
11460 int i, found = FALSE;
11462 player->present = FALSE;
11463 player->active = FALSE;
11465 if (!ExplodeField[jx][jy])
11466 StorePlayer[jx][jy] = 0;
11468 if (player->is_moving)
11469 DrawLevelField(player->last_jx, player->last_jy);
11471 for (i = 0; i < MAX_PLAYERS; i++)
11472 if (stored_player[i].active)
11476 AllPlayersGone = TRUE;
11482 #if USE_NEW_SNAP_DELAY
11483 static void setFieldForSnapping(int x, int y, int element, int direction)
11485 struct ElementInfo *ei = &element_info[element];
11486 int direction_bit = MV_DIR_TO_BIT(direction);
11487 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11488 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11489 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11491 Feld[x][y] = EL_ELEMENT_SNAPPING;
11492 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11494 ResetGfxAnimation(x, y);
11496 GfxElement[x][y] = element;
11497 GfxAction[x][y] = action;
11498 GfxDir[x][y] = direction;
11499 GfxFrame[x][y] = -1;
11504 =============================================================================
11505 checkDiagonalPushing()
11506 -----------------------------------------------------------------------------
11507 check if diagonal input device direction results in pushing of object
11508 (by checking if the alternative direction is walkable, diggable, ...)
11509 =============================================================================
11512 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11513 int x, int y, int real_dx, int real_dy)
11515 int jx, jy, dx, dy, xx, yy;
11517 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11520 /* diagonal direction: check alternative direction */
11525 xx = jx + (dx == 0 ? real_dx : 0);
11526 yy = jy + (dy == 0 ? real_dy : 0);
11528 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11532 =============================================================================
11534 -----------------------------------------------------------------------------
11535 x, y: field next to player (non-diagonal) to try to dig to
11536 real_dx, real_dy: direction as read from input device (can be diagonal)
11537 =============================================================================
11540 int DigField(struct PlayerInfo *player,
11541 int oldx, int oldy, int x, int y,
11542 int real_dx, int real_dy, int mode)
11544 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11545 boolean player_was_pushing = player->is_pushing;
11546 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11547 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11548 int jx = oldx, jy = oldy;
11549 int dx = x - jx, dy = y - jy;
11550 int nextx = x + dx, nexty = y + dy;
11551 int move_direction = (dx == -1 ? MV_LEFT :
11552 dx == +1 ? MV_RIGHT :
11554 dy == +1 ? MV_DOWN : MV_NONE);
11555 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11556 int dig_side = MV_DIR_OPPOSITE(move_direction);
11557 int old_element = Feld[jx][jy];
11558 #if USE_FIXED_DONT_RUN_INTO
11559 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11565 if (is_player) /* function can also be called by EL_PENGUIN */
11567 if (player->MovPos == 0)
11569 player->is_digging = FALSE;
11570 player->is_collecting = FALSE;
11573 if (player->MovPos == 0) /* last pushing move finished */
11574 player->is_pushing = FALSE;
11576 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11578 player->is_switching = FALSE;
11579 player->push_delay = -1;
11581 return MP_NO_ACTION;
11585 #if !USE_FIXED_DONT_RUN_INTO
11586 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11587 return MP_NO_ACTION;
11590 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11591 old_element = Back[jx][jy];
11593 /* in case of element dropped at player position, check background */
11594 else if (Back[jx][jy] != EL_EMPTY &&
11595 game.engine_version >= VERSION_IDENT(2,2,0,0))
11596 old_element = Back[jx][jy];
11599 #if USE_FIXED_DONT_RUN_INTO
11600 if (player_can_move && DONT_RUN_INTO(element))
11602 if (element == EL_ACID && dx == 0 && dy == 1)
11605 Feld[jx][jy] = EL_PLAYER_1;
11606 InitMovingField(jx, jy, MV_DOWN);
11607 Store[jx][jy] = EL_ACID;
11608 ContinueMoving(jx, jy);
11609 BuryPlayer(player);
11612 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11614 return MP_DONT_RUN_INTO;
11620 #if USE_FIXED_DONT_RUN_INTO
11621 if (player_can_move && DONT_RUN_INTO(element))
11623 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11625 return MP_DONT_RUN_INTO;
11630 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11631 return MP_NO_ACTION; /* field has no opening in this direction */
11633 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11634 return MP_NO_ACTION; /* field has no opening in this direction */
11637 #if USE_FIXED_DONT_RUN_INTO
11638 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11641 Feld[jx][jy] = EL_PLAYER_1;
11642 InitMovingField(jx, jy, MV_DOWN);
11643 Store[jx][jy] = EL_ACID;
11644 ContinueMoving(jx, jy);
11645 BuryPlayer(player);
11647 return MP_DONT_RUN_INTO;
11653 #if USE_FIXED_DONT_RUN_INTO
11654 if (player_can_move && DONT_RUN_INTO(element))
11656 if (element == EL_ACID && dx == 0 && dy == 1)
11659 Feld[jx][jy] = EL_PLAYER_1;
11660 InitMovingField(jx, jy, MV_DOWN);
11661 Store[jx][jy] = EL_ACID;
11662 ContinueMoving(jx, jy);
11663 BuryPlayer(player);
11666 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11668 return MP_DONT_RUN_INTO;
11673 #if USE_FIXED_DONT_RUN_INTO
11674 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11675 return MP_NO_ACTION;
11678 #if !USE_FIXED_DONT_RUN_INTO
11679 element = Feld[x][y];
11682 collect_count = element_info[element].collect_count_initial;
11684 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11685 return MP_NO_ACTION;
11687 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11688 player_can_move = player_can_move_or_snap;
11690 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11691 game.engine_version >= VERSION_IDENT(2,2,0,0))
11693 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11694 player->index_bit, dig_side);
11695 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11696 player->index_bit, dig_side);
11698 if (Feld[x][y] != element) /* field changed by snapping */
11701 return MP_NO_ACTION;
11704 if (game.gravity && is_player && !player->is_auto_moving &&
11705 canFallDown(player) && move_direction != MV_DOWN &&
11706 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11707 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11709 if (player_can_move &&
11710 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11712 int sound_element = SND_ELEMENT(element);
11713 int sound_action = ACTION_WALKING;
11715 if (IS_RND_GATE(element))
11717 if (!player->key[RND_GATE_NR(element)])
11718 return MP_NO_ACTION;
11720 else if (IS_RND_GATE_GRAY(element))
11722 if (!player->key[RND_GATE_GRAY_NR(element)])
11723 return MP_NO_ACTION;
11725 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11727 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11728 return MP_NO_ACTION;
11730 else if (element == EL_EXIT_OPEN ||
11731 element == EL_SP_EXIT_OPEN ||
11732 element == EL_SP_EXIT_OPENING)
11734 sound_action = ACTION_PASSING; /* player is passing exit */
11736 else if (element == EL_EMPTY)
11738 sound_action = ACTION_MOVING; /* nothing to walk on */
11741 /* play sound from background or player, whatever is available */
11742 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11743 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11745 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11747 else if (player_can_move &&
11748 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11750 if (!ACCESS_FROM(element, opposite_direction))
11751 return MP_NO_ACTION; /* field not accessible from this direction */
11753 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11754 return MP_NO_ACTION;
11756 if (IS_EM_GATE(element))
11758 if (!player->key[EM_GATE_NR(element)])
11759 return MP_NO_ACTION;
11761 else if (IS_EM_GATE_GRAY(element))
11763 if (!player->key[EM_GATE_GRAY_NR(element)])
11764 return MP_NO_ACTION;
11766 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11768 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11769 return MP_NO_ACTION;
11771 else if (IS_SP_PORT(element))
11773 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11774 element == EL_SP_GRAVITY_PORT_RIGHT ||
11775 element == EL_SP_GRAVITY_PORT_UP ||
11776 element == EL_SP_GRAVITY_PORT_DOWN)
11777 game.gravity = !game.gravity;
11778 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11779 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11780 element == EL_SP_GRAVITY_ON_PORT_UP ||
11781 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11782 game.gravity = TRUE;
11783 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11784 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11785 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11786 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11787 game.gravity = FALSE;
11790 /* automatically move to the next field with double speed */
11791 player->programmed_action = move_direction;
11793 if (player->move_delay_reset_counter == 0)
11795 player->move_delay_reset_counter = 2; /* two double speed steps */
11797 DOUBLE_PLAYER_SPEED(player);
11800 PlayLevelSoundAction(x, y, ACTION_PASSING);
11802 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11806 if (mode != DF_SNAP)
11808 GfxElement[x][y] = GFX_ELEMENT(element);
11809 player->is_digging = TRUE;
11812 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11814 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11815 player->index_bit, dig_side);
11817 if (mode == DF_SNAP)
11819 #if USE_NEW_SNAP_DELAY
11820 if (level.block_snap_field)
11821 setFieldForSnapping(x, y, element, move_direction);
11823 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11825 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11828 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11829 player->index_bit, dig_side);
11832 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11836 if (is_player && mode != DF_SNAP)
11838 GfxElement[x][y] = element;
11839 player->is_collecting = TRUE;
11842 if (element == EL_SPEED_PILL)
11844 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11846 else if (element == EL_EXTRA_TIME && level.time > 0)
11848 TimeLeft += level.extra_time;
11849 DrawGameValue_Time(TimeLeft);
11851 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11853 player->shield_normal_time_left += level.shield_normal_time;
11854 if (element == EL_SHIELD_DEADLY)
11855 player->shield_deadly_time_left += level.shield_deadly_time;
11857 else if (element == EL_DYNAMITE ||
11858 element == EL_EM_DYNAMITE ||
11859 element == EL_SP_DISK_RED)
11861 if (player->inventory_size < MAX_INVENTORY_SIZE)
11862 player->inventory_element[player->inventory_size++] = element;
11865 DrawGameDoorValues();
11867 DrawGameValue_Dynamite(local_player->inventory_size);
11870 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11872 player->dynabomb_count++;
11873 player->dynabombs_left++;
11875 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11877 player->dynabomb_size++;
11879 else if (element == EL_DYNABOMB_INCREASE_POWER)
11881 player->dynabomb_xl = TRUE;
11883 else if (IS_KEY(element))
11885 player->key[KEY_NR(element)] = TRUE;
11888 DrawGameDoorValues();
11890 DrawGameValue_Keys(player->key);
11893 redraw_mask |= REDRAW_DOOR_1;
11895 else if (IS_ENVELOPE(element))
11897 player->show_envelope = element;
11899 else if (element == EL_EMC_LENSES)
11901 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11903 RedrawAllInvisibleElementsForLenses();
11905 else if (element == EL_EMC_MAGNIFIER)
11907 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11909 RedrawAllInvisibleElementsForMagnifier();
11911 else if (IS_DROPPABLE(element) ||
11912 IS_THROWABLE(element)) /* can be collected and dropped */
11916 if (collect_count == 0)
11917 player->inventory_infinite_element = element;
11919 for (i = 0; i < collect_count; i++)
11920 if (player->inventory_size < MAX_INVENTORY_SIZE)
11921 player->inventory_element[player->inventory_size++] = element;
11924 DrawGameDoorValues();
11926 DrawGameValue_Dynamite(local_player->inventory_size);
11929 else if (collect_count > 0)
11931 local_player->gems_still_needed -= collect_count;
11932 if (local_player->gems_still_needed < 0)
11933 local_player->gems_still_needed = 0;
11935 DrawGameValue_Emeralds(local_player->gems_still_needed);
11938 RaiseScoreElement(element);
11939 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11942 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11943 player->index_bit, dig_side);
11945 if (mode == DF_SNAP)
11947 #if USE_NEW_SNAP_DELAY
11948 if (level.block_snap_field)
11949 setFieldForSnapping(x, y, element, move_direction);
11951 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11953 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11956 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11957 player->index_bit, dig_side);
11960 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11962 if (mode == DF_SNAP && element != EL_BD_ROCK)
11963 return MP_NO_ACTION;
11965 if (CAN_FALL(element) && dy)
11966 return MP_NO_ACTION;
11968 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11969 !(element == EL_SPRING && level.use_spring_bug))
11970 return MP_NO_ACTION;
11972 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11973 ((move_direction & MV_VERTICAL &&
11974 ((element_info[element].move_pattern & MV_LEFT &&
11975 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11976 (element_info[element].move_pattern & MV_RIGHT &&
11977 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11978 (move_direction & MV_HORIZONTAL &&
11979 ((element_info[element].move_pattern & MV_UP &&
11980 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11981 (element_info[element].move_pattern & MV_DOWN &&
11982 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11983 return MP_NO_ACTION;
11985 /* do not push elements already moving away faster than player */
11986 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11987 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11988 return MP_NO_ACTION;
11990 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11992 if (player->push_delay_value == -1 || !player_was_pushing)
11993 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11995 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11997 if (player->push_delay_value == -1)
11998 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12000 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12002 if (!player->is_pushing)
12003 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12006 player->is_pushing = TRUE;
12008 if (!(IN_LEV_FIELD(nextx, nexty) &&
12009 (IS_FREE(nextx, nexty) ||
12010 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12011 IS_SB_ELEMENT(element)))))
12012 return MP_NO_ACTION;
12014 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12015 return MP_NO_ACTION;
12017 if (player->push_delay == -1) /* new pushing; restart delay */
12018 player->push_delay = 0;
12020 if (player->push_delay < player->push_delay_value &&
12021 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12022 element != EL_SPRING && element != EL_BALLOON)
12024 /* make sure that there is no move delay before next try to push */
12025 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12026 player->move_delay = 0;
12028 return MP_NO_ACTION;
12031 if (IS_SB_ELEMENT(element))
12033 if (element == EL_SOKOBAN_FIELD_FULL)
12035 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12036 local_player->sokobanfields_still_needed++;
12039 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12041 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12042 local_player->sokobanfields_still_needed--;
12045 Feld[x][y] = EL_SOKOBAN_OBJECT;
12047 if (Back[x][y] == Back[nextx][nexty])
12048 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12049 else if (Back[x][y] != 0)
12050 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12053 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12056 if (local_player->sokobanfields_still_needed == 0 &&
12057 game.emulation == EMU_SOKOBAN)
12059 player->LevelSolved = player->GameOver = TRUE;
12060 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12064 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12066 InitMovingField(x, y, move_direction);
12067 GfxAction[x][y] = ACTION_PUSHING;
12069 if (mode == DF_SNAP)
12070 ContinueMoving(x, y);
12072 MovPos[x][y] = (dx != 0 ? dx : dy);
12074 Pushed[x][y] = TRUE;
12075 Pushed[nextx][nexty] = TRUE;
12077 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12078 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12080 player->push_delay_value = -1; /* get new value later */
12082 /* check for element change _after_ element has been pushed */
12083 if (game.use_change_when_pushing_bug)
12085 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12086 player->index_bit, dig_side);
12087 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12088 player->index_bit, dig_side);
12091 else if (IS_SWITCHABLE(element))
12093 if (PLAYER_SWITCHING(player, x, y))
12095 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12096 player->index_bit, dig_side);
12101 player->is_switching = TRUE;
12102 player->switch_x = x;
12103 player->switch_y = y;
12105 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12107 if (element == EL_ROBOT_WHEEL)
12109 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12113 DrawLevelField(x, y);
12115 else if (element == EL_SP_TERMINAL)
12120 SCAN_PLAYFIELD(xx, yy)
12122 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12125 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12127 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12128 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12131 else if (IS_BELT_SWITCH(element))
12133 ToggleBeltSwitch(x, y);
12135 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12136 element == EL_SWITCHGATE_SWITCH_DOWN)
12138 ToggleSwitchgateSwitch(x, y);
12140 else if (element == EL_LIGHT_SWITCH ||
12141 element == EL_LIGHT_SWITCH_ACTIVE)
12143 ToggleLightSwitch(x, y);
12145 else if (element == EL_TIMEGATE_SWITCH)
12147 ActivateTimegateSwitch(x, y);
12149 else if (element == EL_BALLOON_SWITCH_LEFT ||
12150 element == EL_BALLOON_SWITCH_RIGHT ||
12151 element == EL_BALLOON_SWITCH_UP ||
12152 element == EL_BALLOON_SWITCH_DOWN ||
12153 element == EL_BALLOON_SWITCH_NONE ||
12154 element == EL_BALLOON_SWITCH_ANY)
12156 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12157 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12158 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12159 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12160 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12163 else if (element == EL_LAMP)
12165 Feld[x][y] = EL_LAMP_ACTIVE;
12166 local_player->lights_still_needed--;
12168 ResetGfxAnimation(x, y);
12169 DrawLevelField(x, y);
12171 else if (element == EL_TIME_ORB_FULL)
12173 Feld[x][y] = EL_TIME_ORB_EMPTY;
12175 if (level.time > 0 || level.use_time_orb_bug)
12177 TimeLeft += level.time_orb_time;
12178 DrawGameValue_Time(TimeLeft);
12181 ResetGfxAnimation(x, y);
12182 DrawLevelField(x, y);
12184 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12185 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12189 game.ball_state = !game.ball_state;
12192 SCAN_PLAYFIELD(xx, yy)
12194 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12197 int e = Feld[xx][yy];
12199 if (game.ball_state)
12201 if (e == EL_EMC_MAGIC_BALL)
12202 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12203 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12204 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12208 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12209 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12210 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12211 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12216 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12217 player->index_bit, dig_side);
12219 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12220 player->index_bit, dig_side);
12222 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12223 player->index_bit, dig_side);
12229 if (!PLAYER_SWITCHING(player, x, y))
12231 player->is_switching = TRUE;
12232 player->switch_x = x;
12233 player->switch_y = y;
12235 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12236 player->index_bit, dig_side);
12237 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12238 player->index_bit, dig_side);
12240 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12241 player->index_bit, dig_side);
12242 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12243 player->index_bit, dig_side);
12246 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12247 player->index_bit, dig_side);
12248 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12249 player->index_bit, dig_side);
12251 return MP_NO_ACTION;
12254 player->push_delay = -1;
12256 if (is_player) /* function can also be called by EL_PENGUIN */
12258 if (Feld[x][y] != element) /* really digged/collected something */
12259 player->is_collecting = !player->is_digging;
12265 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12267 int jx = player->jx, jy = player->jy;
12268 int x = jx + dx, y = jy + dy;
12269 int snap_direction = (dx == -1 ? MV_LEFT :
12270 dx == +1 ? MV_RIGHT :
12272 dy == +1 ? MV_DOWN : MV_NONE);
12273 boolean can_continue_snapping = (level.continuous_snapping &&
12274 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12276 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12279 if (!player->active || !IN_LEV_FIELD(x, y))
12287 if (player->MovPos == 0)
12288 player->is_pushing = FALSE;
12290 player->is_snapping = FALSE;
12292 if (player->MovPos == 0)
12294 player->is_moving = FALSE;
12295 player->is_digging = FALSE;
12296 player->is_collecting = FALSE;
12302 #if USE_NEW_CONTINUOUS_SNAPPING
12303 /* prevent snapping with already pressed snap key when not allowed */
12304 if (player->is_snapping && !can_continue_snapping)
12307 if (player->is_snapping)
12311 player->MovDir = snap_direction;
12313 if (player->MovPos == 0)
12315 player->is_moving = FALSE;
12316 player->is_digging = FALSE;
12317 player->is_collecting = FALSE;
12320 player->is_dropping = FALSE;
12321 player->is_dropping_pressed = FALSE;
12322 player->drop_pressed_delay = 0;
12324 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12327 player->is_snapping = TRUE;
12329 if (player->MovPos == 0)
12331 player->is_moving = FALSE;
12332 player->is_digging = FALSE;
12333 player->is_collecting = FALSE;
12336 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12337 DrawLevelField(player->last_jx, player->last_jy);
12339 DrawLevelField(x, y);
12344 boolean DropElement(struct PlayerInfo *player)
12346 int old_element, new_element;
12347 int dropx = player->jx, dropy = player->jy;
12348 int drop_direction = player->MovDir;
12349 int drop_side = drop_direction;
12350 int drop_element = (player->inventory_size > 0 ?
12351 player->inventory_element[player->inventory_size - 1] :
12352 player->inventory_infinite_element != EL_UNDEFINED ?
12353 player->inventory_infinite_element :
12354 player->dynabombs_left > 0 ?
12355 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12358 player->is_dropping_pressed = TRUE;
12360 /* do not drop an element on top of another element; when holding drop key
12361 pressed without moving, dropped element must move away before the next
12362 element can be dropped (this is especially important if the next element
12363 is dynamite, which can be placed on background for historical reasons) */
12364 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12367 if (IS_THROWABLE(drop_element))
12369 dropx += GET_DX_FROM_DIR(drop_direction);
12370 dropy += GET_DY_FROM_DIR(drop_direction);
12372 if (!IN_LEV_FIELD(dropx, dropy))
12376 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12377 new_element = drop_element; /* default: no change when dropping */
12379 /* check if player is active, not moving and ready to drop */
12380 if (!player->active || player->MovPos || player->drop_delay > 0)
12383 /* check if player has anything that can be dropped */
12384 if (new_element == EL_UNDEFINED)
12387 /* check if drop key was pressed long enough for EM style dynamite */
12388 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12391 /* check if anything can be dropped at the current position */
12392 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12395 /* collected custom elements can only be dropped on empty fields */
12396 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12399 if (old_element != EL_EMPTY)
12400 Back[dropx][dropy] = old_element; /* store old element on this field */
12402 ResetGfxAnimation(dropx, dropy);
12403 ResetRandomAnimationValue(dropx, dropy);
12405 if (player->inventory_size > 0 ||
12406 player->inventory_infinite_element != EL_UNDEFINED)
12408 if (player->inventory_size > 0)
12410 player->inventory_size--;
12413 DrawGameDoorValues();
12415 DrawGameValue_Dynamite(local_player->inventory_size);
12418 if (new_element == EL_DYNAMITE)
12419 new_element = EL_DYNAMITE_ACTIVE;
12420 else if (new_element == EL_EM_DYNAMITE)
12421 new_element = EL_EM_DYNAMITE_ACTIVE;
12422 else if (new_element == EL_SP_DISK_RED)
12423 new_element = EL_SP_DISK_RED_ACTIVE;
12426 Feld[dropx][dropy] = new_element;
12428 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12429 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12430 el2img(Feld[dropx][dropy]), 0);
12432 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12434 /* needed if previous element just changed to "empty" in the last frame */
12435 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12437 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12438 player->index_bit, drop_side);
12439 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12441 player->index_bit, drop_side);
12443 TestIfElementTouchesCustomElement(dropx, dropy);
12445 else /* player is dropping a dyna bomb */
12447 player->dynabombs_left--;
12449 Feld[dropx][dropy] = new_element;
12451 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12452 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12453 el2img(Feld[dropx][dropy]), 0);
12455 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12458 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12459 InitField_WithBug1(dropx, dropy, FALSE);
12461 new_element = Feld[dropx][dropy]; /* element might have changed */
12463 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12464 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12466 int move_direction, nextx, nexty;
12468 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12469 MovDir[dropx][dropy] = drop_direction;
12471 move_direction = MovDir[dropx][dropy];
12472 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12473 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12475 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12476 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12479 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12480 player->is_dropping = TRUE;
12482 player->drop_pressed_delay = 0;
12483 player->is_dropping_pressed = FALSE;
12485 player->drop_x = dropx;
12486 player->drop_y = dropy;
12491 /* ------------------------------------------------------------------------- */
12492 /* game sound playing functions */
12493 /* ------------------------------------------------------------------------- */
12495 static int *loop_sound_frame = NULL;
12496 static int *loop_sound_volume = NULL;
12498 void InitPlayLevelSound()
12500 int num_sounds = getSoundListSize();
12502 checked_free(loop_sound_frame);
12503 checked_free(loop_sound_volume);
12505 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12506 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12509 static void PlayLevelSound(int x, int y, int nr)
12511 int sx = SCREENX(x), sy = SCREENY(y);
12512 int volume, stereo_position;
12513 int max_distance = 8;
12514 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12516 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12517 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12520 if (!IN_LEV_FIELD(x, y) ||
12521 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12522 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12525 volume = SOUND_MAX_VOLUME;
12527 if (!IN_SCR_FIELD(sx, sy))
12529 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12530 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12532 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12535 stereo_position = (SOUND_MAX_LEFT +
12536 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12537 (SCR_FIELDX + 2 * max_distance));
12539 if (IS_LOOP_SOUND(nr))
12541 /* This assures that quieter loop sounds do not overwrite louder ones,
12542 while restarting sound volume comparison with each new game frame. */
12544 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12547 loop_sound_volume[nr] = volume;
12548 loop_sound_frame[nr] = FrameCounter;
12551 PlaySoundExt(nr, volume, stereo_position, type);
12554 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12556 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12557 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12558 y < LEVELY(BY1) ? LEVELY(BY1) :
12559 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12563 static void PlayLevelSoundAction(int x, int y, int action)
12565 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12568 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12570 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12572 if (sound_effect != SND_UNDEFINED)
12573 PlayLevelSound(x, y, sound_effect);
12576 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12579 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12581 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12582 PlayLevelSound(x, y, sound_effect);
12585 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12587 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12589 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12590 PlayLevelSound(x, y, sound_effect);
12593 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12595 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12597 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12598 StopSound(sound_effect);
12601 static void PlayLevelMusic()
12603 if (levelset.music[level_nr] != MUS_UNDEFINED)
12604 PlayMusic(levelset.music[level_nr]); /* from config file */
12606 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12609 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12611 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12616 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12620 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12624 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12628 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12632 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12636 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12640 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12643 case SAMPLE_android_clone:
12644 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12647 case SAMPLE_android_move:
12648 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12651 case SAMPLE_spring:
12652 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12656 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12660 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12663 case SAMPLE_eater_eat:
12664 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12668 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12671 case SAMPLE_collect:
12672 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12675 case SAMPLE_diamond:
12676 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12679 case SAMPLE_squash:
12680 /* !!! CHECK THIS !!! */
12682 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12684 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12688 case SAMPLE_wonderfall:
12689 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12693 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12697 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12701 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12705 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12709 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12713 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12716 case SAMPLE_wonder:
12717 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12721 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12724 case SAMPLE_exit_open:
12725 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12728 case SAMPLE_exit_leave:
12729 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12732 case SAMPLE_dynamite:
12733 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12737 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12741 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12745 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12749 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12753 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12757 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12761 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12766 void RaiseScore(int value)
12768 local_player->score += value;
12770 DrawGameValue_Score(local_player->score);
12773 void RaiseScoreElement(int element)
12778 case EL_BD_DIAMOND:
12779 case EL_EMERALD_YELLOW:
12780 case EL_EMERALD_RED:
12781 case EL_EMERALD_PURPLE:
12782 case EL_SP_INFOTRON:
12783 RaiseScore(level.score[SC_EMERALD]);
12786 RaiseScore(level.score[SC_DIAMOND]);
12789 RaiseScore(level.score[SC_CRYSTAL]);
12792 RaiseScore(level.score[SC_PEARL]);
12795 case EL_BD_BUTTERFLY:
12796 case EL_SP_ELECTRON:
12797 RaiseScore(level.score[SC_BUG]);
12800 case EL_BD_FIREFLY:
12801 case EL_SP_SNIKSNAK:
12802 RaiseScore(level.score[SC_SPACESHIP]);
12805 case EL_DARK_YAMYAM:
12806 RaiseScore(level.score[SC_YAMYAM]);
12809 RaiseScore(level.score[SC_ROBOT]);
12812 RaiseScore(level.score[SC_PACMAN]);
12815 RaiseScore(level.score[SC_NUT]);
12818 case EL_EM_DYNAMITE:
12819 case EL_SP_DISK_RED:
12820 case EL_DYNABOMB_INCREASE_NUMBER:
12821 case EL_DYNABOMB_INCREASE_SIZE:
12822 case EL_DYNABOMB_INCREASE_POWER:
12823 RaiseScore(level.score[SC_DYNAMITE]);
12825 case EL_SHIELD_NORMAL:
12826 case EL_SHIELD_DEADLY:
12827 RaiseScore(level.score[SC_SHIELD]);
12829 case EL_EXTRA_TIME:
12830 RaiseScore(level.extra_time_score);
12844 RaiseScore(level.score[SC_KEY]);
12847 RaiseScore(element_info[element].collect_score);
12852 void RequestQuitGame(boolean ask_if_really_quit)
12854 if (AllPlayersGone ||
12855 !ask_if_really_quit ||
12856 level_editor_test_game ||
12857 Request("Do you really want to quit the game ?",
12858 REQ_ASK | REQ_STAY_CLOSED))
12860 #if defined(NETWORK_AVALIABLE)
12861 if (options.network)
12862 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12866 game_status = GAME_MODE_MAIN;
12872 if (tape.playing && tape.deactivate_display)
12873 TapeDeactivateDisplayOff(TRUE);
12875 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12877 if (tape.playing && tape.deactivate_display)
12878 TapeDeactivateDisplayOn();
12883 /* ---------- new game button stuff ---------------------------------------- */
12885 /* graphic position values for game buttons */
12886 #define GAME_BUTTON_XSIZE 30
12887 #define GAME_BUTTON_YSIZE 30
12888 #define GAME_BUTTON_XPOS 5
12889 #define GAME_BUTTON_YPOS 215
12890 #define SOUND_BUTTON_XPOS 5
12891 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12893 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12894 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12895 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12896 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12897 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12898 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12905 } gamebutton_info[NUM_GAME_BUTTONS] =
12908 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12913 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12914 GAME_CTRL_ID_PAUSE,
12918 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12923 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12924 SOUND_CTRL_ID_MUSIC,
12925 "background music on/off"
12928 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12929 SOUND_CTRL_ID_LOOPS,
12930 "sound loops on/off"
12933 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12934 SOUND_CTRL_ID_SIMPLE,
12935 "normal sounds on/off"
12939 void CreateGameButtons()
12943 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12945 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12946 struct GadgetInfo *gi;
12949 unsigned long event_mask;
12950 int gd_xoffset, gd_yoffset;
12951 int gd_x1, gd_x2, gd_y1, gd_y2;
12954 gd_xoffset = gamebutton_info[i].x;
12955 gd_yoffset = gamebutton_info[i].y;
12956 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12957 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12959 if (id == GAME_CTRL_ID_STOP ||
12960 id == GAME_CTRL_ID_PAUSE ||
12961 id == GAME_CTRL_ID_PLAY)
12963 button_type = GD_TYPE_NORMAL_BUTTON;
12965 event_mask = GD_EVENT_RELEASED;
12966 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12967 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12971 button_type = GD_TYPE_CHECK_BUTTON;
12973 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12974 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12975 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12976 event_mask = GD_EVENT_PRESSED;
12977 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12978 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12981 gi = CreateGadget(GDI_CUSTOM_ID, id,
12982 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12983 GDI_X, DX + gd_xoffset,
12984 GDI_Y, DY + gd_yoffset,
12985 GDI_WIDTH, GAME_BUTTON_XSIZE,
12986 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12987 GDI_TYPE, button_type,
12988 GDI_STATE, GD_BUTTON_UNPRESSED,
12989 GDI_CHECKED, checked,
12990 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12991 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12992 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12993 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12994 GDI_EVENT_MASK, event_mask,
12995 GDI_CALLBACK_ACTION, HandleGameButtons,
12999 Error(ERR_EXIT, "cannot create gadget");
13001 game_gadget[id] = gi;
13005 void FreeGameButtons()
13009 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13010 FreeGadget(game_gadget[i]);
13013 static void MapGameButtons()
13017 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13018 MapGadget(game_gadget[i]);
13021 void UnmapGameButtons()
13025 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13026 UnmapGadget(game_gadget[i]);
13029 static void HandleGameButtons(struct GadgetInfo *gi)
13031 int id = gi->custom_id;
13033 if (game_status != GAME_MODE_PLAYING)
13038 case GAME_CTRL_ID_STOP:
13042 RequestQuitGame(TRUE);
13045 case GAME_CTRL_ID_PAUSE:
13046 if (options.network)
13048 #if defined(NETWORK_AVALIABLE)
13050 SendToServer_ContinuePlaying();
13052 SendToServer_PausePlaying();
13056 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13059 case GAME_CTRL_ID_PLAY:
13062 #if defined(NETWORK_AVALIABLE)
13063 if (options.network)
13064 SendToServer_ContinuePlaying();
13068 tape.pausing = FALSE;
13069 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13074 case SOUND_CTRL_ID_MUSIC:
13075 if (setup.sound_music)
13077 setup.sound_music = FALSE;
13080 else if (audio.music_available)
13082 setup.sound = setup.sound_music = TRUE;
13084 SetAudioMode(setup.sound);
13090 case SOUND_CTRL_ID_LOOPS:
13091 if (setup.sound_loops)
13092 setup.sound_loops = FALSE;
13093 else if (audio.loops_available)
13095 setup.sound = setup.sound_loops = TRUE;
13096 SetAudioMode(setup.sound);
13100 case SOUND_CTRL_ID_SIMPLE:
13101 if (setup.sound_simple)
13102 setup.sound_simple = FALSE;
13103 else if (audio.sound_available)
13105 setup.sound = setup.sound_simple = TRUE;
13106 SetAudioMode(setup.sound);