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)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
57 /* for MovePlayer() */
58 #define MP_NO_ACTION 0
61 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
63 /* for ScrollPlayer() */
65 #define SCROLL_GO_ON 1
67 /* for Bang()/Explode() */
68 #define EX_PHASE_START 0
69 #define EX_TYPE_NONE 0
70 #define EX_TYPE_NORMAL (1 << 0)
71 #define EX_TYPE_CENTER (1 << 1)
72 #define EX_TYPE_BORDER (1 << 2)
73 #define EX_TYPE_CROSS (1 << 3)
74 #define EX_TYPE_DYNA (1 << 4)
75 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
77 /* special positions in the game control window (relative to control window) */
80 #define XX_EMERALDS 29
81 #define YY_EMERALDS 54
82 #define XX_DYNAMITE 29
83 #define YY_DYNAMITE 89
92 /* special positions in the game control window (relative to main window) */
93 #define DX_LEVEL (DX + XX_LEVEL)
94 #define DY_LEVEL (DY + YY_LEVEL)
95 #define DX_EMERALDS (DX + XX_EMERALDS)
96 #define DY_EMERALDS (DY + YY_EMERALDS)
97 #define DX_DYNAMITE (DX + XX_DYNAMITE)
98 #define DY_DYNAMITE (DY + YY_DYNAMITE)
99 #define DX_KEYS (DX + XX_KEYS)
100 #define DY_KEYS (DY + YY_KEYS)
101 #define DX_SCORE (DX + XX_SCORE)
102 #define DY_SCORE (DY + YY_SCORE)
103 #define DX_TIME1 (DX + XX_TIME1)
104 #define DX_TIME2 (DX + XX_TIME2)
105 #define DY_TIME (DY + YY_TIME)
107 /* values for delayed check of falling and moving elements and for collision */
108 #define CHECK_DELAY_MOVING 3
109 #define CHECK_DELAY_FALLING 3
110 #define CHECK_DELAY_COLLISION 2
112 /* values for initial player move delay (initial delay counter value) */
113 #define INITIAL_MOVE_DELAY_OFF -1
114 #define INITIAL_MOVE_DELAY_ON 0
116 /* values for player movement speed (which is in fact a delay value) */
117 #define MOVE_DELAY_MIN_SPEED 32
118 #define MOVE_DELAY_NORMAL_SPEED 8
119 #define MOVE_DELAY_HIGH_SPEED 4
120 #define MOVE_DELAY_MAX_SPEED 1
123 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
124 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
126 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
127 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
129 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
130 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
132 /* values for other actions */
133 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
134 #define MOVE_STEPSIZE_MIN (1)
135 #define MOVE_STEPSIZE_MAX (TILEX)
137 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
138 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
140 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
142 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
143 RND(element_info[e].push_delay_random))
144 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
145 RND(element_info[e].drop_delay_random))
146 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
147 RND(element_info[e].move_delay_random))
148 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
149 (element_info[e].move_delay_random))
150 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
151 RND(element_info[e].ce_value_random_initial))
152 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
153 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
154 RND((c)->delay_random * (c)->delay_frames))
155 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
156 RND((c)->delay_random))
160 #define GET_VALID_RUNTIME_ELEMENT(e) \
161 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
163 #define GET_VALID_FILE_ELEMENT(e) \
164 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
167 #define GET_TARGET_ELEMENT(e, ch, cv, cs) \
168 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
169 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
170 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
171 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
172 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
173 (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
175 #define CAN_GROW_INTO(e) \
176 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
182 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
183 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
184 (CAN_MOVE_INTO_ACID(e) && \
185 Feld[x][y] == EL_ACID) || \
188 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
189 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
190 (CAN_MOVE_INTO_ACID(e) && \
191 Feld[x][y] == EL_ACID) || \
194 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
195 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
197 (CAN_MOVE_INTO_ACID(e) && \
198 Feld[x][y] == EL_ACID) || \
199 (DONT_COLLIDE_WITH(e) && \
201 !PLAYER_ENEMY_PROTECTED(x, y))))
203 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
204 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
206 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
209 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
212 #define ANDROID_CAN_CLONE_FIELD(x, y) \
213 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
214 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
216 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
219 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
220 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
222 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
223 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
225 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
226 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
228 #define PIG_CAN_ENTER_FIELD(e, x, y) \
229 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
231 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
233 IS_FOOD_PENGUIN(Feld[x][y])))
234 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
237 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
240 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
243 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
244 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
245 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
248 #define GROUP_NR(e) ((e) - EL_GROUP_START)
249 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
250 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
252 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
253 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
256 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
258 #define CE_ENTER_FIELD_COND(e, x, y) \
259 (!IS_PLAYER(x, y) && \
260 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
262 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
263 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
265 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
266 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
268 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
269 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
270 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
271 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
273 /* game button identifiers */
274 #define GAME_CTRL_ID_STOP 0
275 #define GAME_CTRL_ID_PAUSE 1
276 #define GAME_CTRL_ID_PLAY 2
277 #define SOUND_CTRL_ID_MUSIC 3
278 #define SOUND_CTRL_ID_LOOPS 4
279 #define SOUND_CTRL_ID_SIMPLE 5
281 #define NUM_GAME_BUTTONS 6
284 /* forward declaration for internal use */
286 static void CreateField(int, int, int);
288 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
289 static void AdvanceFrameAndPlayerCounters(int);
291 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
292 static boolean MovePlayer(struct PlayerInfo *, int, int);
293 static void ScrollPlayer(struct PlayerInfo *, int);
294 static void ScrollScreen(struct PlayerInfo *, int);
296 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
298 static void InitBeltMovement(void);
299 static void CloseAllOpenTimegates(void);
300 static void CheckGravityMovement(struct PlayerInfo *);
301 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
302 static void KillPlayerUnlessEnemyProtected(int, int);
303 static void KillPlayerUnlessExplosionProtected(int, int);
305 static void TestIfPlayerTouchesCustomElement(int, int);
306 static void TestIfElementTouchesCustomElement(int, int);
307 static void TestIfElementHitsCustomElement(int, int, int);
309 static void TestIfElementSmashesCustomElement(int, int, int);
312 static void HandleElementChange(int, int, int);
313 static void ExecuteCustomElementAction(int, int, int, int);
314 static boolean ChangeElement(int, int, int, int);
316 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
317 #define CheckTriggeredElementChange(x, y, e, ev) \
318 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
319 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
321 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
323 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
324 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
326 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
327 #define CheckElementChange(x, y, e, te, ev) \
328 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
329 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
330 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
331 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
332 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
334 static void PlayLevelSound(int, int, int);
335 static void PlayLevelSoundNearest(int, int, int);
336 static void PlayLevelSoundAction(int, int, int);
337 static void PlayLevelSoundElementAction(int, int, int, int);
338 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
339 static void PlayLevelSoundActionIfLoop(int, int, int);
340 static void StopLevelSoundActionIfLoop(int, int, int);
341 static void PlayLevelMusic();
343 static void MapGameButtons();
344 static void HandleGameButtons(struct GadgetInfo *);
346 int AmoebeNachbarNr(int, int);
347 void AmoebeUmwandeln(int, int);
348 void ContinueMoving(int, int);
350 void InitMovDir(int, int);
351 void InitAmoebaNr(int, int);
352 int NewHiScore(void);
354 void TestIfGoodThingHitsBadThing(int, int, int);
355 void TestIfBadThingHitsGoodThing(int, int, int);
356 void TestIfPlayerTouchesBadThing(int, int);
357 void TestIfPlayerRunsIntoBadThing(int, int, int);
358 void TestIfBadThingTouchesPlayer(int, int);
359 void TestIfBadThingRunsIntoPlayer(int, int, int);
360 void TestIfFriendTouchesBadThing(int, int);
361 void TestIfBadThingTouchesFriend(int, int);
362 void TestIfBadThingTouchesOtherBadThing(int, int);
364 void KillPlayer(struct PlayerInfo *);
365 void BuryPlayer(struct PlayerInfo *);
366 void RemovePlayer(struct PlayerInfo *);
368 boolean SnapField(struct PlayerInfo *, int, int);
369 boolean DropElement(struct PlayerInfo *);
371 static int getInvisibleActiveFromInvisibleElement(int);
372 static int getInvisibleFromInvisibleActiveElement(int);
374 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
377 /* ------------------------------------------------------------------------- */
378 /* definition of elements that automatically change to other elements after */
379 /* a specified time, eventually calling a function when changing */
380 /* ------------------------------------------------------------------------- */
382 /* forward declaration for changer functions */
383 static void InitBuggyBase(int, int);
384 static void WarnBuggyBase(int, int);
386 static void InitTrap(int, int);
387 static void ActivateTrap(int, int);
388 static void ChangeActiveTrap(int, int);
390 static void InitRobotWheel(int, int);
391 static void RunRobotWheel(int, int);
392 static void StopRobotWheel(int, int);
394 static void InitTimegateWheel(int, int);
395 static void RunTimegateWheel(int, int);
397 static void InitMagicBallDelay(int, int);
398 static void ActivateMagicBall(int, int);
400 static void InitDiagonalMovingElement(int, int);
402 struct ChangingElementInfo
407 void (*pre_change_function)(int x, int y);
408 void (*change_function)(int x, int y);
409 void (*post_change_function)(int x, int y);
412 static struct ChangingElementInfo change_delay_list[] =
463 EL_SWITCHGATE_OPENING,
471 EL_SWITCHGATE_CLOSING,
472 EL_SWITCHGATE_CLOSED,
504 EL_ACID_SPLASH_RIGHT,
513 EL_SP_BUGGY_BASE_ACTIVATING,
520 EL_SP_BUGGY_BASE_ACTIVATING,
521 EL_SP_BUGGY_BASE_ACTIVE,
528 EL_SP_BUGGY_BASE_ACTIVE,
552 EL_ROBOT_WHEEL_ACTIVE,
560 EL_TIMEGATE_SWITCH_ACTIVE,
568 EL_EMC_MAGIC_BALL_ACTIVE,
569 EL_EMC_MAGIC_BALL_ACTIVE,
576 EL_EMC_SPRING_BUMPER_ACTIVE,
577 EL_EMC_SPRING_BUMPER,
584 EL_DIAGONAL_SHRINKING,
597 InitDiagonalMovingElement
613 int push_delay_fixed, push_delay_random;
618 { EL_BALLOON, 0, 0 },
620 { EL_SOKOBAN_OBJECT, 2, 0 },
621 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
622 { EL_SATELLITE, 2, 0 },
623 { EL_SP_DISK_YELLOW, 2, 0 },
625 { EL_UNDEFINED, 0, 0 },
633 move_stepsize_list[] =
635 { EL_AMOEBA_DROP, 2 },
636 { EL_AMOEBA_DROPPING, 2 },
637 { EL_QUICKSAND_FILLING, 1 },
638 { EL_QUICKSAND_EMPTYING, 1 },
639 { EL_MAGIC_WALL_FILLING, 2 },
640 { EL_BD_MAGIC_WALL_FILLING, 2 },
641 { EL_MAGIC_WALL_EMPTYING, 2 },
642 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
652 collect_count_list[] =
655 { EL_BD_DIAMOND, 1 },
656 { EL_EMERALD_YELLOW, 1 },
657 { EL_EMERALD_RED, 1 },
658 { EL_EMERALD_PURPLE, 1 },
660 { EL_SP_INFOTRON, 1 },
672 access_direction_list[] =
674 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
675 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
676 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
677 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
678 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
679 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
680 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
681 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
682 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
683 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
684 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
686 { EL_SP_PORT_LEFT, MV_RIGHT },
687 { EL_SP_PORT_RIGHT, MV_LEFT },
688 { EL_SP_PORT_UP, MV_DOWN },
689 { EL_SP_PORT_DOWN, MV_UP },
690 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
691 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
692 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
693 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
694 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
695 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
696 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
697 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
698 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
699 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
700 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
701 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
702 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
703 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
704 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
706 { EL_UNDEFINED, MV_NONE }
709 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
711 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
712 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
713 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
714 IS_JUST_CHANGING(x, y))
716 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
718 /* static variables for playfield scan mode (scanning forward or backward) */
719 static int playfield_scan_start_x = 0;
720 static int playfield_scan_start_y = 0;
721 static int playfield_scan_delta_x = 1;
722 static int playfield_scan_delta_y = 1;
724 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
725 (y) >= 0 && (y) <= lev_fieldy - 1; \
726 (y) += playfield_scan_delta_y) \
727 for ((x) = playfield_scan_start_x; \
728 (x) >= 0 && (x) <= lev_fieldx - 1; \
729 (x) += playfield_scan_delta_x) \
732 void DEBUG_SetMaximumDynamite()
736 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
737 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
738 local_player->inventory_element[local_player->inventory_size++] =
743 static void InitPlayfieldScanModeVars()
745 if (game.use_reverse_scan_direction)
747 playfield_scan_start_x = lev_fieldx - 1;
748 playfield_scan_start_y = lev_fieldy - 1;
750 playfield_scan_delta_x = -1;
751 playfield_scan_delta_y = -1;
755 playfield_scan_start_x = 0;
756 playfield_scan_start_y = 0;
758 playfield_scan_delta_x = 1;
759 playfield_scan_delta_y = 1;
763 static void InitPlayfieldScanMode(int mode)
765 game.use_reverse_scan_direction =
766 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
768 InitPlayfieldScanModeVars();
771 static int get_move_delay_from_stepsize(int move_stepsize)
774 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
776 /* make sure that stepsize value is always a power of 2 */
777 move_stepsize = (1 << log_2(move_stepsize));
779 return TILEX / move_stepsize;
782 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
785 int player_nr = player->index_nr;
786 int move_delay = get_move_delay_from_stepsize(move_stepsize);
787 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
789 /* do no immediately change move delay -- the player might just be moving */
790 player->move_delay_value_next = move_delay;
792 /* information if player can move must be set separately */
793 player->cannot_move = cannot_move;
797 player->move_delay = game.initial_move_delay[player_nr];
798 player->move_delay_value = game.initial_move_delay_value[player_nr];
800 player->move_delay_value_next = -1;
802 player->move_delay_reset_counter = 0;
806 void GetPlayerConfig()
808 if (!audio.sound_available)
809 setup.sound_simple = FALSE;
811 if (!audio.loops_available)
812 setup.sound_loops = FALSE;
814 if (!audio.music_available)
815 setup.sound_music = FALSE;
817 if (!video.fullscreen_available)
818 setup.fullscreen = FALSE;
820 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
822 SetAudioMode(setup.sound);
826 static int getBeltNrFromBeltElement(int element)
828 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
829 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
830 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
833 static int getBeltNrFromBeltActiveElement(int element)
835 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
836 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
837 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
840 static int getBeltNrFromBeltSwitchElement(int element)
842 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
843 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
844 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
847 static int getBeltDirNrFromBeltSwitchElement(int element)
849 static int belt_base_element[4] =
851 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
852 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
853 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
854 EL_CONVEYOR_BELT_4_SWITCH_LEFT
857 int belt_nr = getBeltNrFromBeltSwitchElement(element);
858 int belt_dir_nr = element - belt_base_element[belt_nr];
860 return (belt_dir_nr % 3);
863 static int getBeltDirFromBeltSwitchElement(int element)
865 static int belt_move_dir[3] =
872 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
874 return belt_move_dir[belt_dir_nr];
877 static int get_element_from_group_element(int element)
879 if (IS_GROUP_ELEMENT(element))
881 struct ElementGroupInfo *group = element_info[element].group;
882 int last_anim_random_frame = gfx.anim_random_frame;
885 if (group->choice_mode == ANIM_RANDOM)
886 gfx.anim_random_frame = RND(group->num_elements_resolved);
888 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
889 group->choice_mode, 0,
892 if (group->choice_mode == ANIM_RANDOM)
893 gfx.anim_random_frame = last_anim_random_frame;
897 element = group->element_resolved[element_pos];
903 static void InitPlayerField(int x, int y, int element, boolean init_game)
905 if (element == EL_SP_MURPHY)
909 if (stored_player[0].present)
911 Feld[x][y] = EL_SP_MURPHY_CLONE;
917 stored_player[0].use_murphy = TRUE;
919 if (!level.use_artwork_element[0])
920 stored_player[0].artwork_element = EL_SP_MURPHY;
923 Feld[x][y] = EL_PLAYER_1;
929 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
930 int jx = player->jx, jy = player->jy;
932 player->present = TRUE;
934 player->block_last_field = (element == EL_SP_MURPHY ?
935 level.sp_block_last_field :
936 level.block_last_field);
938 /* ---------- initialize player's last field block delay --------------- */
940 /* always start with reliable default value (no adjustment needed) */
941 player->block_delay_adjustment = 0;
943 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
944 if (player->block_last_field && element == EL_SP_MURPHY)
945 player->block_delay_adjustment = 1;
947 /* special case 2: in game engines before 3.1.1, blocking was different */
948 if (game.use_block_last_field_bug)
949 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
951 if (!options.network || player->connected)
953 player->active = TRUE;
955 /* remove potentially duplicate players */
956 if (StorePlayer[jx][jy] == Feld[x][y])
957 StorePlayer[jx][jy] = 0;
959 StorePlayer[x][y] = Feld[x][y];
963 printf("Player %d activated.\n", player->element_nr);
964 printf("[Local player is %d and currently %s.]\n",
965 local_player->element_nr,
966 local_player->active ? "active" : "not active");
970 Feld[x][y] = EL_EMPTY;
972 player->jx = player->last_jx = x;
973 player->jy = player->last_jy = y;
977 static void InitField(int x, int y, boolean init_game)
979 int element = Feld[x][y];
988 InitPlayerField(x, y, element, init_game);
991 case EL_SOKOBAN_FIELD_PLAYER:
992 element = Feld[x][y] = EL_PLAYER_1;
993 InitField(x, y, init_game);
995 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
996 InitField(x, y, init_game);
999 case EL_SOKOBAN_FIELD_EMPTY:
1000 local_player->sokobanfields_still_needed++;
1004 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1005 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1006 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1007 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1008 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1009 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1010 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1011 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1012 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1013 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1022 case EL_SPACESHIP_RIGHT:
1023 case EL_SPACESHIP_UP:
1024 case EL_SPACESHIP_LEFT:
1025 case EL_SPACESHIP_DOWN:
1026 case EL_BD_BUTTERFLY:
1027 case EL_BD_BUTTERFLY_RIGHT:
1028 case EL_BD_BUTTERFLY_UP:
1029 case EL_BD_BUTTERFLY_LEFT:
1030 case EL_BD_BUTTERFLY_DOWN:
1032 case EL_BD_FIREFLY_RIGHT:
1033 case EL_BD_FIREFLY_UP:
1034 case EL_BD_FIREFLY_LEFT:
1035 case EL_BD_FIREFLY_DOWN:
1036 case EL_PACMAN_RIGHT:
1038 case EL_PACMAN_LEFT:
1039 case EL_PACMAN_DOWN:
1041 case EL_YAMYAM_LEFT:
1042 case EL_YAMYAM_RIGHT:
1044 case EL_YAMYAM_DOWN:
1045 case EL_DARK_YAMYAM:
1048 case EL_SP_SNIKSNAK:
1049 case EL_SP_ELECTRON:
1058 case EL_AMOEBA_FULL:
1063 case EL_AMOEBA_DROP:
1064 if (y == lev_fieldy - 1)
1066 Feld[x][y] = EL_AMOEBA_GROWING;
1067 Store[x][y] = EL_AMOEBA_WET;
1071 case EL_DYNAMITE_ACTIVE:
1072 case EL_SP_DISK_RED_ACTIVE:
1073 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1074 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1075 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1076 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1077 MovDelay[x][y] = 96;
1080 case EL_EM_DYNAMITE_ACTIVE:
1081 MovDelay[x][y] = 32;
1085 local_player->lights_still_needed++;
1089 local_player->friends_still_needed++;
1094 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1097 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1098 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1099 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1100 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1101 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1102 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1103 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1104 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1105 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1106 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1107 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1108 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1111 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1112 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1113 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1115 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1117 game.belt_dir[belt_nr] = belt_dir;
1118 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1120 else /* more than one switch -- set it like the first switch */
1122 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1127 #if !USE_BOTH_SWITCHGATE_SWITCHES
1128 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1130 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1134 case EL_LIGHT_SWITCH_ACTIVE:
1136 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1139 case EL_INVISIBLE_STEELWALL:
1140 case EL_INVISIBLE_WALL:
1141 case EL_INVISIBLE_SAND:
1142 if (game.light_time_left > 0 ||
1143 game.lenses_time_left > 0)
1144 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1147 case EL_EMC_MAGIC_BALL:
1148 if (game.ball_state)
1149 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1152 case EL_EMC_MAGIC_BALL_SWITCH:
1153 if (game.ball_state)
1154 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1159 if (IS_CUSTOM_ELEMENT(element))
1161 if (CAN_MOVE(element))
1164 #if USE_NEW_CUSTOM_VALUE
1165 if (!element_info[element].use_last_ce_value || init_game)
1166 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1170 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1173 else if (IS_GROUP_ELEMENT(element))
1176 Feld[x][y] = get_element_from_group_element(element);
1178 InitField(x, y, init_game);
1180 struct ElementGroupInfo *group = element_info[element].group;
1181 int last_anim_random_frame = gfx.anim_random_frame;
1184 if (group->choice_mode == ANIM_RANDOM)
1185 gfx.anim_random_frame = RND(group->num_elements_resolved);
1187 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1188 group->choice_mode, 0,
1191 if (group->choice_mode == ANIM_RANDOM)
1192 gfx.anim_random_frame = last_anim_random_frame;
1194 group->choice_pos++;
1196 Feld[x][y] = group->element_resolved[element_pos];
1198 InitField(x, y, init_game);
1206 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1211 #if USE_NEW_CUSTOM_VALUE
1214 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1216 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1224 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1226 InitField(x, y, init_game);
1228 /* not needed to call InitMovDir() -- already done by InitField()! */
1229 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1230 CAN_MOVE(Feld[x][y]))
1234 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1236 int old_element = Feld[x][y];
1238 InitField(x, y, init_game);
1240 /* not needed to call InitMovDir() -- already done by InitField()! */
1241 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1242 CAN_MOVE(old_element) &&
1243 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1246 /* this case is in fact a combination of not less than three bugs:
1247 first, it calls InitMovDir() for elements that can move, although this is
1248 already done by InitField(); then, it checks the element that was at this
1249 field _before_ the call to InitField() (which can change it); lastly, it
1250 was not called for "mole with direction" elements, which were treated as
1251 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1255 inline void DrawGameValue_Emeralds(int value)
1257 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1259 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1262 inline void DrawGameValue_Dynamite(int value)
1264 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1266 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1269 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1271 int base_key_graphic = EL_KEY_1;
1274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1275 base_key_graphic = EL_EM_KEY_1;
1277 /* currently only 4 of 8 possible keys are displayed */
1278 for (i = 0; i < STD_NUM_KEYS; i++)
1281 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1282 el2edimg(base_key_graphic + i));
1284 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1285 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1286 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1290 inline void DrawGameValue_Score(int value)
1292 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1294 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1297 inline void DrawGameValue_Time(int value)
1299 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1300 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1302 /* clear background if value just changed its size */
1303 if (value == 999 || value == 1000)
1304 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1307 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1309 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1312 inline void DrawGameValue_Level(int value)
1315 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1318 /* misuse area for displaying emeralds to draw bigger level number */
1319 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1320 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1322 /* now copy it to the area for displaying level number */
1323 BlitBitmap(drawto, drawto,
1324 DX_EMERALDS, DY_EMERALDS + 1,
1325 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1326 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1327 DX_LEVEL - 1, DY_LEVEL + 1);
1329 /* restore the area for displaying emeralds */
1330 DrawGameValue_Emeralds(local_player->gems_still_needed);
1332 /* yes, this is all really ugly :-) */
1336 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1339 int key[MAX_NUM_KEYS];
1342 for (i = 0; i < MAX_NUM_KEYS; i++)
1343 key[i] = key_bits & (1 << i);
1345 DrawGameValue_Level(level_nr);
1347 DrawGameValue_Emeralds(emeralds);
1348 DrawGameValue_Dynamite(dynamite);
1349 DrawGameValue_Score(score);
1350 DrawGameValue_Time(time);
1352 DrawGameValue_Keys(key);
1355 void DrawGameDoorValues()
1357 int dynamite_state = 0;
1361 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1363 DrawGameDoorValues_EM();
1369 DrawGameValue_Level(level_nr);
1371 DrawGameValue_Emeralds(local_player->gems_still_needed);
1372 DrawGameValue_Dynamite(local_player->inventory_size);
1373 DrawGameValue_Score(local_player->score);
1374 DrawGameValue_Time(TimeLeft);
1378 if (game.centered_player_nr == -1)
1380 for (i = 0; i < MAX_PLAYERS; i++)
1382 for (j = 0; j < MAX_NUM_KEYS; j++)
1383 if (stored_player[i].key[j])
1384 key_bits |= (1 << j);
1386 dynamite_state += stored_player[i].inventory_size;
1390 DrawGameValue_Keys(stored_player[i].key);
1395 int player_nr = game.centered_player_nr;
1397 for (i = 0; i < MAX_NUM_KEYS; i++)
1398 if (stored_player[player_nr].key[i])
1399 key_bits |= (1 << i);
1401 dynamite_state = stored_player[player_nr].inventory_size;
1404 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1405 local_player->score, TimeLeft, key_bits);
1410 static void resolve_group_element(int group_element, int recursion_depth)
1412 static int group_nr;
1413 static struct ElementGroupInfo *group;
1414 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1417 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1419 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1420 group_element - EL_GROUP_START + 1);
1422 /* replace element which caused too deep recursion by question mark */
1423 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1428 if (recursion_depth == 0) /* initialization */
1430 group = element_info[group_element].group;
1431 group_nr = group_element - EL_GROUP_START;
1433 group->num_elements_resolved = 0;
1434 group->choice_pos = 0;
1437 for (i = 0; i < actual_group->num_elements; i++)
1439 int element = actual_group->element[i];
1441 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1444 if (IS_GROUP_ELEMENT(element))
1445 resolve_group_element(element, recursion_depth + 1);
1448 group->element_resolved[group->num_elements_resolved++] = element;
1449 element_info[element].in_group[group_nr] = TRUE;
1456 =============================================================================
1458 -----------------------------------------------------------------------------
1459 initialize game engine due to level / tape version number
1460 =============================================================================
1463 static void InitGameEngine()
1465 int i, j, k, l, x, y;
1467 /* set game engine from tape file when re-playing, else from level file */
1468 game.engine_version = (tape.playing ? tape.engine_version :
1469 level.game_version);
1471 /* ---------------------------------------------------------------------- */
1472 /* set flags for bugs and changes according to active game engine version */
1473 /* ---------------------------------------------------------------------- */
1476 Summary of bugfix/change:
1477 Fixed handling for custom elements that change when pushed by the player.
1479 Fixed/changed in version:
1483 Before 3.1.0, custom elements that "change when pushing" changed directly
1484 after the player started pushing them (until then handled in "DigField()").
1485 Since 3.1.0, these custom elements are not changed until the "pushing"
1486 move of the element is finished (now handled in "ContinueMoving()").
1488 Affected levels/tapes:
1489 The first condition is generally needed for all levels/tapes before version
1490 3.1.0, which might use the old behaviour before it was changed; known tapes
1491 that are affected are some tapes from the level set "Walpurgis Gardens" by
1493 The second condition is an exception from the above case and is needed for
1494 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1495 above (including some development versions of 3.1.0), but before it was
1496 known that this change would break tapes like the above and was fixed in
1497 3.1.1, so that the changed behaviour was active although the engine version
1498 while recording maybe was before 3.1.0. There is at least one tape that is
1499 affected by this exception, which is the tape for the one-level set "Bug
1500 Machine" by Juergen Bonhagen.
1503 game.use_change_when_pushing_bug =
1504 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1506 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1507 tape.game_version < VERSION_IDENT(3,1,1,0)));
1510 Summary of bugfix/change:
1511 Fixed handling for blocking the field the player leaves when moving.
1513 Fixed/changed in version:
1517 Before 3.1.1, when "block last field when moving" was enabled, the field
1518 the player is leaving when moving was blocked for the time of the move,
1519 and was directly unblocked afterwards. This resulted in the last field
1520 being blocked for exactly one less than the number of frames of one player
1521 move. Additionally, even when blocking was disabled, the last field was
1522 blocked for exactly one frame.
1523 Since 3.1.1, due to changes in player movement handling, the last field
1524 is not blocked at all when blocking is disabled. When blocking is enabled,
1525 the last field is blocked for exactly the number of frames of one player
1526 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1527 last field is blocked for exactly one more than the number of frames of
1530 Affected levels/tapes:
1531 (!!! yet to be determined -- probably many !!!)
1534 game.use_block_last_field_bug =
1535 (game.engine_version < VERSION_IDENT(3,1,1,0));
1538 Summary of bugfix/change:
1539 Changed behaviour of CE changes with multiple changes per single frame.
1541 Fixed/changed in version:
1545 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1546 This resulted in race conditions where CEs seem to behave strange in some
1547 situations (where triggered CE changes were just skipped because there was
1548 already a CE change on that tile in the playfield in that engine frame).
1549 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1550 (The number of changes per frame must be limited in any case, because else
1551 it is easily possible to define CE changes that would result in an infinite
1552 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1553 should be set large enough so that it would only be reached in cases where
1554 the corresponding CE change conditions run into a loop. Therefore, it seems
1555 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1556 maximal number of change pages for custom elements.)
1558 Affected levels/tapes:
1562 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1563 game.max_num_changes_per_frame = 1;
1565 game.max_num_changes_per_frame =
1566 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1569 /* ---------------------------------------------------------------------- */
1571 /* default scan direction: scan playfield from top/left to bottom/right */
1572 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1574 /* dynamically adjust element properties according to game engine version */
1575 InitElementPropertiesEngine(game.engine_version);
1578 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1579 printf(" tape version == %06d [%s] [file: %06d]\n",
1580 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1582 printf(" => game.engine_version == %06d\n", game.engine_version);
1586 /* ---------- recursively resolve group elements ------------------------- */
1588 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1589 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1590 element_info[i].in_group[j] = FALSE;
1592 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1593 resolve_group_element(EL_GROUP_START + i, 0);
1596 /* ---------- initialize player's initial move delay --------------------- */
1599 /* dynamically adjust player properties according to level information */
1600 for (i = 0; i < MAX_PLAYERS; i++)
1601 game.initial_move_delay_value[i] =
1602 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1604 /* dynamically adjust player properties according to level information */
1605 game.initial_move_delay_value =
1606 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1609 /* dynamically adjust player properties according to game engine version */
1610 for (i = 0; i < MAX_PLAYERS; i++)
1611 game.initial_move_delay[i] =
1612 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1613 game.initial_move_delay_value[i] : 0);
1615 /* ---------- initialize player's initial push delay --------------------- */
1617 /* dynamically adjust player properties according to game engine version */
1618 game.initial_push_delay_value =
1619 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1621 /* ---------- initialize changing elements ------------------------------- */
1623 /* initialize changing elements information */
1624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1626 struct ElementInfo *ei = &element_info[i];
1628 /* this pointer might have been changed in the level editor */
1629 ei->change = &ei->change_page[0];
1631 if (!IS_CUSTOM_ELEMENT(i))
1633 ei->change->target_element = EL_EMPTY_SPACE;
1634 ei->change->delay_fixed = 0;
1635 ei->change->delay_random = 0;
1636 ei->change->delay_frames = 1;
1639 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1641 ei->has_change_event[j] = FALSE;
1643 ei->event_page_nr[j] = 0;
1644 ei->event_page[j] = &ei->change_page[0];
1648 /* add changing elements from pre-defined list */
1649 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1651 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1652 struct ElementInfo *ei = &element_info[ch_delay->element];
1654 ei->change->target_element = ch_delay->target_element;
1655 ei->change->delay_fixed = ch_delay->change_delay;
1657 ei->change->pre_change_function = ch_delay->pre_change_function;
1658 ei->change->change_function = ch_delay->change_function;
1659 ei->change->post_change_function = ch_delay->post_change_function;
1661 ei->change->can_change = TRUE;
1662 ei->change->can_change_or_has_action = TRUE;
1664 ei->has_change_event[CE_DELAY] = TRUE;
1666 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1667 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1670 /* ---------- initialize internal run-time variables ------------- */
1672 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1674 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1676 for (j = 0; j < ei->num_change_pages; j++)
1678 ei->change_page[j].can_change_or_has_action =
1679 (ei->change_page[j].can_change |
1680 ei->change_page[j].has_action);
1684 /* add change events from custom element configuration */
1685 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1687 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1689 for (j = 0; j < ei->num_change_pages; j++)
1691 if (!ei->change_page[j].can_change_or_has_action)
1694 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1696 /* only add event page for the first page found with this event */
1697 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1699 ei->has_change_event[k] = TRUE;
1701 ei->event_page_nr[k] = j;
1702 ei->event_page[k] = &ei->change_page[j];
1708 /* ---------- initialize run-time trigger player and element ------------- */
1710 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1712 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1714 for (j = 0; j < ei->num_change_pages; j++)
1716 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1717 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1718 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1719 ei->change_page[j].actual_trigger_ce_value = 0;
1720 ei->change_page[j].actual_trigger_ce_score = 0;
1724 /* ---------- initialize trigger events ---------------------------------- */
1726 /* initialize trigger events information */
1727 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1728 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1729 trigger_events[i][j] = FALSE;
1731 /* add trigger events from element change event properties */
1732 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1734 struct ElementInfo *ei = &element_info[i];
1736 for (j = 0; j < ei->num_change_pages; j++)
1738 if (!ei->change_page[j].can_change_or_has_action)
1741 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1743 int trigger_element = ei->change_page[j].trigger_element;
1745 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1747 if (ei->change_page[j].has_event[k])
1749 if (IS_GROUP_ELEMENT(trigger_element))
1751 struct ElementGroupInfo *group =
1752 element_info[trigger_element].group;
1754 for (l = 0; l < group->num_elements_resolved; l++)
1755 trigger_events[group->element_resolved[l]][k] = TRUE;
1758 trigger_events[trigger_element][k] = TRUE;
1765 /* ---------- initialize push delay -------------------------------------- */
1767 /* initialize push delay values to default */
1768 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1770 if (!IS_CUSTOM_ELEMENT(i))
1773 /* set default push delay values (corrected since version 3.0.7-1) */
1774 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1776 element_info[i].push_delay_fixed = 2;
1777 element_info[i].push_delay_random = 8;
1781 element_info[i].push_delay_fixed = 8;
1782 element_info[i].push_delay_random = 8;
1785 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1786 element_info[i].push_delay_random = game.default_push_delay_random;
1791 /* set push delay value for certain elements from pre-defined list */
1792 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1794 int e = push_delay_list[i].element;
1796 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1797 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1800 /* set push delay value for Supaplex elements for newer engine versions */
1801 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1803 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1805 if (IS_SP_ELEMENT(i))
1807 /* set SP push delay to just enough to push under a falling zonk */
1808 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1810 element_info[i].push_delay_fixed = delay;
1811 element_info[i].push_delay_random = 0;
1816 /* ---------- initialize move stepsize ----------------------------------- */
1818 /* initialize move stepsize values to default */
1819 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1820 if (!IS_CUSTOM_ELEMENT(i))
1821 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1823 /* set move stepsize value for certain elements from pre-defined list */
1824 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1826 int e = move_stepsize_list[i].element;
1828 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1831 /* ---------- initialize collect score ----------------------------------- */
1833 /* initialize collect score values for custom elements from initial value */
1834 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1835 if (IS_CUSTOM_ELEMENT(i))
1836 element_info[i].collect_score = element_info[i].collect_score_initial;
1838 /* ---------- initialize collect count ----------------------------------- */
1840 /* initialize collect count values for non-custom elements */
1841 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1842 if (!IS_CUSTOM_ELEMENT(i))
1843 element_info[i].collect_count_initial = 0;
1845 /* add collect count values for all elements from pre-defined list */
1846 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1847 element_info[collect_count_list[i].element].collect_count_initial =
1848 collect_count_list[i].count;
1850 /* ---------- initialize access direction -------------------------------- */
1852 /* initialize access direction values to default (access from every side) */
1853 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1854 if (!IS_CUSTOM_ELEMENT(i))
1855 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1857 /* set access direction value for certain elements from pre-defined list */
1858 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1859 element_info[access_direction_list[i].element].access_direction =
1860 access_direction_list[i].direction;
1862 /* ---------- initialize explosion content ------------------------------- */
1863 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1865 if (IS_CUSTOM_ELEMENT(i))
1868 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1870 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1872 element_info[i].content.e[x][y] =
1873 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1874 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1875 i == EL_PLAYER_3 ? EL_EMERALD :
1876 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1877 i == EL_MOLE ? EL_EMERALD_RED :
1878 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1879 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1880 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1881 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1882 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1883 i == EL_WALL_EMERALD ? EL_EMERALD :
1884 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1885 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1886 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1887 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1888 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1889 i == EL_WALL_PEARL ? EL_PEARL :
1890 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1896 int get_num_special_action(int element, int action_first, int action_last)
1898 int num_special_action = 0;
1901 for (i = action_first; i <= action_last; i++)
1903 boolean found = FALSE;
1905 for (j = 0; j < NUM_DIRECTIONS; j++)
1906 if (el_act_dir2img(element, i, j) !=
1907 el_act_dir2img(element, ACTION_DEFAULT, j))
1911 num_special_action++;
1917 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1920 return num_special_action;
1924 =============================================================================
1926 -----------------------------------------------------------------------------
1927 initialize and start new game
1928 =============================================================================
1933 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1934 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1935 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1940 /* don't play tapes over network */
1941 network_playing = (options.network && !tape.playing);
1943 for (i = 0; i < MAX_PLAYERS; i++)
1945 struct PlayerInfo *player = &stored_player[i];
1947 player->index_nr = i;
1948 player->index_bit = (1 << i);
1949 player->element_nr = EL_PLAYER_1 + i;
1951 player->present = FALSE;
1952 player->active = FALSE;
1955 player->effective_action = 0;
1956 player->programmed_action = 0;
1959 player->gems_still_needed = level.gems_needed;
1960 player->sokobanfields_still_needed = 0;
1961 player->lights_still_needed = 0;
1962 player->friends_still_needed = 0;
1964 for (j = 0; j < MAX_NUM_KEYS; j++)
1965 player->key[j] = FALSE;
1967 player->dynabomb_count = 0;
1968 player->dynabomb_size = 1;
1969 player->dynabombs_left = 0;
1970 player->dynabomb_xl = FALSE;
1972 player->MovDir = MV_NONE;
1975 player->GfxDir = MV_NONE;
1976 player->GfxAction = ACTION_DEFAULT;
1978 player->StepFrame = 0;
1980 player->use_murphy = FALSE;
1981 player->artwork_element =
1982 (level.use_artwork_element[i] ? level.artwork_element[i] :
1983 player->element_nr);
1985 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1986 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1988 player->gravity = level.initial_player_gravity[i];
1990 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1992 player->actual_frame_counter = 0;
1994 player->step_counter = 0;
1996 player->last_move_dir = MV_NONE;
1998 player->is_active = FALSE;
2000 player->is_waiting = FALSE;
2001 player->is_moving = FALSE;
2002 player->is_auto_moving = FALSE;
2003 player->is_digging = FALSE;
2004 player->is_snapping = FALSE;
2005 player->is_collecting = FALSE;
2006 player->is_pushing = FALSE;
2007 player->is_switching = FALSE;
2008 player->is_dropping = FALSE;
2009 player->is_dropping_pressed = FALSE;
2011 player->is_bored = FALSE;
2012 player->is_sleeping = FALSE;
2014 player->frame_counter_bored = -1;
2015 player->frame_counter_sleeping = -1;
2017 player->anim_delay_counter = 0;
2018 player->post_delay_counter = 0;
2020 player->dir_waiting = MV_NONE;
2021 player->action_waiting = ACTION_DEFAULT;
2022 player->last_action_waiting = ACTION_DEFAULT;
2023 player->special_action_bored = ACTION_DEFAULT;
2024 player->special_action_sleeping = ACTION_DEFAULT;
2027 /* cannot be set here -- could be modified in Init[Player]Field() below */
2029 /* set number of special actions for bored and sleeping animation */
2030 player->num_special_action_bored =
2031 get_num_special_action(player->artwork_element,
2032 ACTION_BORING_1, ACTION_BORING_LAST);
2033 player->num_special_action_sleeping =
2034 get_num_special_action(player->artwork_element,
2035 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2038 player->switch_x = -1;
2039 player->switch_y = -1;
2041 player->drop_x = -1;
2042 player->drop_y = -1;
2044 player->show_envelope = 0;
2047 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2049 player->move_delay = game.initial_move_delay;
2050 player->move_delay_value = game.initial_move_delay_value;
2052 player->move_delay_value_next = -1;
2054 player->move_delay_reset_counter = 0;
2056 player->cannot_move = FALSE;
2059 player->push_delay = -1; /* initialized when pushing starts */
2060 player->push_delay_value = game.initial_push_delay_value;
2062 player->drop_delay = 0;
2063 player->drop_pressed_delay = 0;
2065 player->last_jx = player->last_jy = 0;
2066 player->jx = player->jy = 0;
2068 player->shield_normal_time_left = 0;
2069 player->shield_deadly_time_left = 0;
2071 player->inventory_infinite_element = EL_UNDEFINED;
2072 player->inventory_size = 0;
2074 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2075 SnapField(player, 0, 0);
2077 player->LevelSolved = FALSE;
2078 player->GameOver = FALSE;
2081 network_player_action_received = FALSE;
2083 #if defined(NETWORK_AVALIABLE)
2084 /* initial null action */
2085 if (network_playing)
2086 SendToServer_MovePlayer(MV_NONE);
2095 TimeLeft = level.time;
2098 ScreenMovDir = MV_NONE;
2102 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2104 AllPlayersGone = FALSE;
2106 game.yamyam_content_nr = 0;
2107 game.magic_wall_active = FALSE;
2108 game.magic_wall_time_left = 0;
2109 game.light_time_left = 0;
2110 game.timegate_time_left = 0;
2111 game.switchgate_pos = 0;
2112 game.wind_direction = level.wind_direction_initial;
2114 #if !USE_PLAYER_GRAVITY
2116 game.gravity = FALSE;
2118 game.gravity = level.initial_gravity;
2120 game.explosions_delayed = TRUE;
2123 game.lenses_time_left = 0;
2124 game.magnify_time_left = 0;
2126 game.ball_state = level.ball_state_initial;
2127 game.ball_content_nr = 0;
2129 game.envelope_active = FALSE;
2131 /* set focus to local player for network games, else to all players */
2132 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2133 game.centered_player_nr_next = game.centered_player_nr;
2134 game.set_centered_player = FALSE;
2136 if (network_playing && tape.recording)
2138 /* store client dependent player focus when recording network games */
2139 tape.centered_player_nr_next = game.centered_player_nr_next;
2140 tape.set_centered_player = TRUE;
2144 printf("::: focus set to player %d [%d]\n",
2145 game.centered_player_nr, local_player->index_nr);
2148 for (i = 0; i < NUM_BELTS; i++)
2150 game.belt_dir[i] = MV_NONE;
2151 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2154 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2155 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2158 SCAN_PLAYFIELD(x, y)
2160 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2163 Feld[x][y] = level.field[x][y];
2164 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2165 ChangeDelay[x][y] = 0;
2166 ChangePage[x][y] = -1;
2167 #if USE_NEW_CUSTOM_VALUE
2168 CustomValue[x][y] = 0; /* initialized in InitField() */
2170 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2172 WasJustMoving[x][y] = 0;
2173 WasJustFalling[x][y] = 0;
2174 CheckCollision[x][y] = 0;
2176 Pushed[x][y] = FALSE;
2178 ChangeCount[x][y] = 0;
2179 ChangeEvent[x][y] = -1;
2181 ExplodePhase[x][y] = 0;
2182 ExplodeDelay[x][y] = 0;
2183 ExplodeField[x][y] = EX_TYPE_NONE;
2185 RunnerVisit[x][y] = 0;
2186 PlayerVisit[x][y] = 0;
2189 GfxRandom[x][y] = INIT_GFX_RANDOM();
2190 GfxElement[x][y] = EL_UNDEFINED;
2191 GfxAction[x][y] = ACTION_DEFAULT;
2192 GfxDir[x][y] = MV_NONE;
2196 SCAN_PLAYFIELD(x, y)
2198 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2201 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2203 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2205 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2208 InitField(x, y, TRUE);
2213 for (i = 0; i < MAX_PLAYERS; i++)
2215 struct PlayerInfo *player = &stored_player[i];
2218 /* set number of special actions for bored and sleeping animation */
2219 player->num_special_action_bored =
2220 get_num_special_action(player->artwork_element,
2221 ACTION_BORING_1, ACTION_BORING_LAST);
2222 player->num_special_action_sleeping =
2223 get_num_special_action(player->artwork_element,
2224 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2229 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2230 emulate_sb ? EMU_SOKOBAN :
2231 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2233 #if USE_NEW_ALL_SLIPPERY
2234 /* initialize type of slippery elements */
2235 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2237 if (!IS_CUSTOM_ELEMENT(i))
2239 /* default: elements slip down either to the left or right randomly */
2240 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2242 /* SP style elements prefer to slip down on the left side */
2243 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2244 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2246 /* BD style elements prefer to slip down on the left side */
2247 if (game.emulation == EMU_BOULDERDASH)
2248 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2253 /* initialize explosion and ignition delay */
2254 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2256 if (!IS_CUSTOM_ELEMENT(i))
2259 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2260 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2261 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2262 int last_phase = (num_phase + 1) * delay;
2263 int half_phase = (num_phase / 2) * delay;
2265 element_info[i].explosion_delay = last_phase - 1;
2266 element_info[i].ignition_delay = half_phase;
2268 if (i == EL_BLACK_ORB)
2269 element_info[i].ignition_delay = 1;
2273 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2274 element_info[i].explosion_delay = 1;
2276 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2277 element_info[i].ignition_delay = 1;
2281 /* correct non-moving belts to start moving left */
2282 for (i = 0; i < NUM_BELTS; i++)
2283 if (game.belt_dir[i] == MV_NONE)
2284 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2286 /* check if any connected player was not found in playfield */
2287 for (i = 0; i < MAX_PLAYERS; i++)
2289 struct PlayerInfo *player = &stored_player[i];
2291 if (player->connected && !player->present)
2293 for (j = 0; j < MAX_PLAYERS; j++)
2295 struct PlayerInfo *some_player = &stored_player[j];
2296 int jx = some_player->jx, jy = some_player->jy;
2298 /* assign first free player found that is present in the playfield */
2299 if (some_player->present && !some_player->connected)
2301 player->present = TRUE;
2302 player->active = TRUE;
2304 some_player->present = FALSE;
2305 some_player->active = FALSE;
2308 player->element_nr = some_player->element_nr;
2311 player->artwork_element = some_player->artwork_element;
2313 player->block_last_field = some_player->block_last_field;
2314 player->block_delay_adjustment = some_player->block_delay_adjustment;
2316 StorePlayer[jx][jy] = player->element_nr;
2317 player->jx = player->last_jx = jx;
2318 player->jy = player->last_jy = jy;
2328 /* when playing a tape, eliminate all players who do not participate */
2330 for (i = 0; i < MAX_PLAYERS; i++)
2332 if (stored_player[i].active && !tape.player_participates[i])
2334 struct PlayerInfo *player = &stored_player[i];
2335 int jx = player->jx, jy = player->jy;
2337 player->active = FALSE;
2338 StorePlayer[jx][jy] = 0;
2339 Feld[jx][jy] = EL_EMPTY;
2343 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2345 /* when in single player mode, eliminate all but the first active player */
2347 for (i = 0; i < MAX_PLAYERS; i++)
2349 if (stored_player[i].active)
2351 for (j = i + 1; j < MAX_PLAYERS; j++)
2353 if (stored_player[j].active)
2355 struct PlayerInfo *player = &stored_player[j];
2356 int jx = player->jx, jy = player->jy;
2358 player->active = FALSE;
2359 player->present = FALSE;
2361 StorePlayer[jx][jy] = 0;
2362 Feld[jx][jy] = EL_EMPTY;
2369 /* when recording the game, store which players take part in the game */
2372 for (i = 0; i < MAX_PLAYERS; i++)
2373 if (stored_player[i].active)
2374 tape.player_participates[i] = TRUE;
2379 for (i = 0; i < MAX_PLAYERS; i++)
2381 struct PlayerInfo *player = &stored_player[i];
2383 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2388 if (local_player == player)
2389 printf("Player %d is local player.\n", i+1);
2393 if (BorderElement == EL_EMPTY)
2396 SBX_Right = lev_fieldx - SCR_FIELDX;
2398 SBY_Lower = lev_fieldy - SCR_FIELDY;
2403 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2405 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2408 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2409 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2411 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2412 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2414 /* if local player not found, look for custom element that might create
2415 the player (make some assumptions about the right custom element) */
2416 if (!local_player->present)
2418 int start_x = 0, start_y = 0;
2419 int found_rating = 0;
2420 int found_element = EL_UNDEFINED;
2421 int player_nr = local_player->index_nr;
2424 SCAN_PLAYFIELD(x, y)
2426 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2429 int element = Feld[x][y];
2434 if (level.use_start_element[player_nr] &&
2435 level.start_element[player_nr] == element &&
2442 found_element = element;
2445 if (!IS_CUSTOM_ELEMENT(element))
2448 if (CAN_CHANGE(element))
2450 for (i = 0; i < element_info[element].num_change_pages; i++)
2452 /* check for player created from custom element as single target */
2453 content = element_info[element].change_page[i].target_element;
2454 is_player = ELEM_IS_PLAYER(content);
2456 if (is_player && (found_rating < 3 || element < found_element))
2462 found_element = element;
2467 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2469 /* check for player created from custom element as explosion content */
2470 content = element_info[element].content.e[xx][yy];
2471 is_player = ELEM_IS_PLAYER(content);
2473 if (is_player && (found_rating < 2 || element < found_element))
2475 start_x = x + xx - 1;
2476 start_y = y + yy - 1;
2479 found_element = element;
2482 if (!CAN_CHANGE(element))
2485 for (i = 0; i < element_info[element].num_change_pages; i++)
2487 /* check for player created from custom element as extended target */
2489 element_info[element].change_page[i].target_content.e[xx][yy];
2491 is_player = ELEM_IS_PLAYER(content);
2493 if (is_player && (found_rating < 1 || element < found_element))
2495 start_x = x + xx - 1;
2496 start_y = y + yy - 1;
2499 found_element = element;
2505 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2506 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2509 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2510 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2515 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2516 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2517 local_player->jx - MIDPOSX);
2519 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2520 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2521 local_player->jy - MIDPOSY);
2524 if (!game.restart_level)
2525 CloseDoor(DOOR_CLOSE_1);
2527 /* !!! FIX THIS (START) !!! */
2528 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2530 InitGameEngine_EM();
2537 /* after drawing the level, correct some elements */
2538 if (game.timegate_time_left == 0)
2539 CloseAllOpenTimegates();
2541 if (setup.soft_scrolling)
2542 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2544 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2547 /* !!! FIX THIS (END) !!! */
2549 if (!game.restart_level)
2551 /* copy default game door content to main double buffer */
2552 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2553 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2556 DrawGameDoorValues();
2558 if (!game.restart_level)
2562 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2563 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2564 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2568 /* copy actual game door content to door double buffer for OpenDoor() */
2569 BlitBitmap(drawto, bitmap_db_door,
2570 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2572 OpenDoor(DOOR_OPEN_ALL);
2574 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2576 if (setup.sound_music)
2579 KeyboardAutoRepeatOffUnlessAutoplay();
2583 for (i = 0; i < MAX_PLAYERS; i++)
2584 printf("Player %d %sactive.\n",
2585 i + 1, (stored_player[i].active ? "" : "not "));
2589 game.restart_level = FALSE;
2592 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2594 /* this is used for non-R'n'D game engines to update certain engine values */
2596 /* needed to determine if sounds are played within the visible screen area */
2597 scroll_x = actual_scroll_x;
2598 scroll_y = actual_scroll_y;
2601 void InitMovDir(int x, int y)
2603 int i, element = Feld[x][y];
2604 static int xy[4][2] =
2611 static int direction[3][4] =
2613 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2614 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2615 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2624 Feld[x][y] = EL_BUG;
2625 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2628 case EL_SPACESHIP_RIGHT:
2629 case EL_SPACESHIP_UP:
2630 case EL_SPACESHIP_LEFT:
2631 case EL_SPACESHIP_DOWN:
2632 Feld[x][y] = EL_SPACESHIP;
2633 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2636 case EL_BD_BUTTERFLY_RIGHT:
2637 case EL_BD_BUTTERFLY_UP:
2638 case EL_BD_BUTTERFLY_LEFT:
2639 case EL_BD_BUTTERFLY_DOWN:
2640 Feld[x][y] = EL_BD_BUTTERFLY;
2641 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2644 case EL_BD_FIREFLY_RIGHT:
2645 case EL_BD_FIREFLY_UP:
2646 case EL_BD_FIREFLY_LEFT:
2647 case EL_BD_FIREFLY_DOWN:
2648 Feld[x][y] = EL_BD_FIREFLY;
2649 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2652 case EL_PACMAN_RIGHT:
2654 case EL_PACMAN_LEFT:
2655 case EL_PACMAN_DOWN:
2656 Feld[x][y] = EL_PACMAN;
2657 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2660 case EL_YAMYAM_LEFT:
2661 case EL_YAMYAM_RIGHT:
2663 case EL_YAMYAM_DOWN:
2664 Feld[x][y] = EL_YAMYAM;
2665 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2668 case EL_SP_SNIKSNAK:
2669 MovDir[x][y] = MV_UP;
2672 case EL_SP_ELECTRON:
2673 MovDir[x][y] = MV_LEFT;
2680 Feld[x][y] = EL_MOLE;
2681 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2685 if (IS_CUSTOM_ELEMENT(element))
2687 struct ElementInfo *ei = &element_info[element];
2688 int move_direction_initial = ei->move_direction_initial;
2689 int move_pattern = ei->move_pattern;
2691 if (move_direction_initial == MV_START_PREVIOUS)
2693 if (MovDir[x][y] != MV_NONE)
2696 move_direction_initial = MV_START_AUTOMATIC;
2699 if (move_direction_initial == MV_START_RANDOM)
2700 MovDir[x][y] = 1 << RND(4);
2701 else if (move_direction_initial & MV_ANY_DIRECTION)
2702 MovDir[x][y] = move_direction_initial;
2703 else if (move_pattern == MV_ALL_DIRECTIONS ||
2704 move_pattern == MV_TURNING_LEFT ||
2705 move_pattern == MV_TURNING_RIGHT ||
2706 move_pattern == MV_TURNING_LEFT_RIGHT ||
2707 move_pattern == MV_TURNING_RIGHT_LEFT ||
2708 move_pattern == MV_TURNING_RANDOM)
2709 MovDir[x][y] = 1 << RND(4);
2710 else if (move_pattern == MV_HORIZONTAL)
2711 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2712 else if (move_pattern == MV_VERTICAL)
2713 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2714 else if (move_pattern & MV_ANY_DIRECTION)
2715 MovDir[x][y] = element_info[element].move_pattern;
2716 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2717 move_pattern == MV_ALONG_RIGHT_SIDE)
2719 /* use random direction as default start direction */
2720 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2721 MovDir[x][y] = 1 << RND(4);
2723 for (i = 0; i < NUM_DIRECTIONS; i++)
2725 int x1 = x + xy[i][0];
2726 int y1 = y + xy[i][1];
2728 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2730 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2731 MovDir[x][y] = direction[0][i];
2733 MovDir[x][y] = direction[1][i];
2742 MovDir[x][y] = 1 << RND(4);
2744 if (element != EL_BUG &&
2745 element != EL_SPACESHIP &&
2746 element != EL_BD_BUTTERFLY &&
2747 element != EL_BD_FIREFLY)
2750 for (i = 0; i < NUM_DIRECTIONS; i++)
2752 int x1 = x + xy[i][0];
2753 int y1 = y + xy[i][1];
2755 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2757 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2759 MovDir[x][y] = direction[0][i];
2762 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2763 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2765 MovDir[x][y] = direction[1][i];
2774 GfxDir[x][y] = MovDir[x][y];
2777 void InitAmoebaNr(int x, int y)
2780 int group_nr = AmoebeNachbarNr(x, y);
2784 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2786 if (AmoebaCnt[i] == 0)
2794 AmoebaNr[x][y] = group_nr;
2795 AmoebaCnt[group_nr]++;
2796 AmoebaCnt2[group_nr]++;
2802 boolean raise_level = FALSE;
2804 if (local_player->MovPos)
2807 if (tape.auto_play) /* tape might already be stopped here */
2808 tape.auto_play_level_solved = TRUE;
2810 local_player->LevelSolved = FALSE;
2812 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2816 if (!tape.playing && setup.sound_loops)
2817 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2818 SND_CTRL_PLAY_LOOP);
2820 while (TimeLeft > 0)
2822 if (!tape.playing && !setup.sound_loops)
2823 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2825 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2828 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2833 RaiseScore(level.score[SC_TIME_BONUS]);
2836 DrawGameValue_Time(TimeLeft);
2844 if (!tape.playing && setup.sound_loops)
2845 StopSound(SND_GAME_LEVELTIME_BONUS);
2847 else if (level.time == 0) /* level without time limit */
2849 if (!tape.playing && setup.sound_loops)
2850 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2851 SND_CTRL_PLAY_LOOP);
2853 while (TimePlayed < 999)
2855 if (!tape.playing && !setup.sound_loops)
2856 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2858 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2861 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2866 RaiseScore(level.score[SC_TIME_BONUS]);
2869 DrawGameValue_Time(TimePlayed);
2877 if (!tape.playing && setup.sound_loops)
2878 StopSound(SND_GAME_LEVELTIME_BONUS);
2881 /* close exit door after last player */
2882 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2883 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2884 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2886 int element = Feld[ExitX][ExitY];
2888 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2889 EL_SP_EXIT_CLOSING);
2891 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2894 /* player disappears */
2895 if (ExitX >= 0 && ExitY >= 0)
2896 DrawLevelField(ExitX, ExitY);
2902 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2904 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2910 CloseDoor(DOOR_CLOSE_1);
2915 SaveTape(tape.level_nr); /* Ask to save tape */
2918 if (level_nr == leveldir_current->handicap_level)
2920 leveldir_current->handicap_level++;
2921 SaveLevelSetup_SeriesInfo();
2924 if (level_editor_test_game)
2925 local_player->score = -1; /* no highscore when playing from editor */
2926 else if (level_nr < leveldir_current->last_level)
2927 raise_level = TRUE; /* advance to next level */
2929 if ((hi_pos = NewHiScore()) >= 0)
2931 game_status = GAME_MODE_SCORES;
2932 DrawHallOfFame(hi_pos);
2941 game_status = GAME_MODE_MAIN;
2958 LoadScore(level_nr);
2960 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2961 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2964 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2966 if (local_player->score > highscore[k].Score)
2968 /* player has made it to the hall of fame */
2970 if (k < MAX_SCORE_ENTRIES - 1)
2972 int m = MAX_SCORE_ENTRIES - 1;
2975 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2976 if (strEqual(setup.player_name, highscore[l].Name))
2978 if (m == k) /* player's new highscore overwrites his old one */
2982 for (l = m; l > k; l--)
2984 strcpy(highscore[l].Name, highscore[l - 1].Name);
2985 highscore[l].Score = highscore[l - 1].Score;
2992 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2993 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2994 highscore[k].Score = local_player->score;
3000 else if (!strncmp(setup.player_name, highscore[k].Name,
3001 MAX_PLAYER_NAME_LEN))
3002 break; /* player already there with a higher score */
3008 SaveScore(level_nr);
3013 inline static int getElementMoveStepsize(int x, int y)
3015 int element = Feld[x][y];
3016 int direction = MovDir[x][y];
3017 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3018 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3019 int horiz_move = (dx != 0);
3020 int sign = (horiz_move ? dx : dy);
3021 int step = sign * element_info[element].move_stepsize;
3023 /* special values for move stepsize for spring and things on conveyor belt */
3027 if (element == EL_SPRING)
3028 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3029 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3030 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3031 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3033 if (CAN_FALL(element) &&
3034 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3035 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3036 else if (element == EL_SPRING)
3037 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3044 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3046 if (player->GfxAction != action || player->GfxDir != dir)
3049 printf("Player frame reset! (%d => %d, %d => %d)\n",
3050 player->GfxAction, action, player->GfxDir, dir);
3053 player->GfxAction = action;
3054 player->GfxDir = dir;
3056 player->StepFrame = 0;
3060 #if USE_GFX_RESET_GFX_ANIMATION
3061 static void ResetGfxFrame(int x, int y, boolean redraw)
3063 int element = Feld[x][y];
3064 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3065 int last_gfx_frame = GfxFrame[x][y];
3067 if (graphic_info[graphic].anim_global_sync)
3068 GfxFrame[x][y] = FrameCounter;
3069 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3070 GfxFrame[x][y] = CustomValue[x][y];
3071 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3072 GfxFrame[x][y] = element_info[element].collect_score;
3073 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3074 GfxFrame[x][y] = ChangeDelay[x][y];
3076 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3077 DrawLevelGraphicAnimation(x, y, graphic);
3081 static void ResetGfxAnimation(int x, int y)
3084 int element, graphic;
3087 GfxAction[x][y] = ACTION_DEFAULT;
3088 GfxDir[x][y] = MovDir[x][y];
3092 element = Feld[x][y];
3093 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3095 if (graphic_info[graphic].anim_global_sync)
3096 GfxFrame[x][y] = FrameCounter;
3097 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3098 GfxFrame[x][y] = CustomValue[x][y];
3099 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3100 GfxFrame[x][y] = element_info[element].collect_score;
3101 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3102 GfxFrame[x][y] = ChangeDelay[x][y];
3105 #if USE_GFX_RESET_GFX_ANIMATION
3106 ResetGfxFrame(x, y, FALSE);
3110 static void ResetRandomAnimationValue(int x, int y)
3112 GfxRandom[x][y] = INIT_GFX_RANDOM();
3115 void InitMovingField(int x, int y, int direction)
3117 int element = Feld[x][y];
3121 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3122 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3126 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3127 ResetGfxAnimation(x, y);
3129 MovDir[x][y] = direction;
3130 GfxDir[x][y] = direction;
3131 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3132 ACTION_FALLING : ACTION_MOVING);
3135 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3137 if (graphic_info[graphic].anim_global_sync)
3138 GfxFrame[x][y] = FrameCounter;
3139 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3140 GfxFrame[x][y] = CustomValue[x][y];
3141 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3142 GfxFrame[x][y] = element_info[element].collect_score;
3143 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3144 GfxFrame[x][y] = ChangeDelay[x][y];
3147 /* this is needed for CEs with property "can move" / "not moving" */
3149 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3151 if (Feld[newx][newy] == EL_EMPTY)
3152 Feld[newx][newy] = EL_BLOCKED;
3154 MovDir[newx][newy] = MovDir[x][y];
3156 #if USE_NEW_CUSTOM_VALUE
3157 CustomValue[newx][newy] = CustomValue[x][y];
3160 GfxFrame[newx][newy] = GfxFrame[x][y];
3161 GfxRandom[newx][newy] = GfxRandom[x][y];
3162 GfxAction[newx][newy] = GfxAction[x][y];
3163 GfxDir[newx][newy] = GfxDir[x][y];
3167 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3169 int direction = MovDir[x][y];
3171 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3172 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3174 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3175 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3182 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3184 int oldx = x, oldy = y;
3185 int direction = MovDir[x][y];
3187 if (direction == MV_LEFT)
3189 else if (direction == MV_RIGHT)
3191 else if (direction == MV_UP)
3193 else if (direction == MV_DOWN)
3196 *comes_from_x = oldx;
3197 *comes_from_y = oldy;
3200 int MovingOrBlocked2Element(int x, int y)
3202 int element = Feld[x][y];
3204 if (element == EL_BLOCKED)
3208 Blocked2Moving(x, y, &oldx, &oldy);
3209 return Feld[oldx][oldy];
3215 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3217 /* like MovingOrBlocked2Element(), but if element is moving
3218 and (x,y) is the field the moving element is just leaving,
3219 return EL_BLOCKED instead of the element value */
3220 int element = Feld[x][y];
3222 if (IS_MOVING(x, y))
3224 if (element == EL_BLOCKED)
3228 Blocked2Moving(x, y, &oldx, &oldy);
3229 return Feld[oldx][oldy];
3238 static void RemoveField(int x, int y)
3240 Feld[x][y] = EL_EMPTY;
3246 #if USE_NEW_CUSTOM_VALUE
3247 CustomValue[x][y] = 0;
3251 ChangeDelay[x][y] = 0;
3252 ChangePage[x][y] = -1;
3253 Pushed[x][y] = FALSE;
3256 ExplodeField[x][y] = EX_TYPE_NONE;
3259 GfxElement[x][y] = EL_UNDEFINED;
3260 GfxAction[x][y] = ACTION_DEFAULT;
3261 GfxDir[x][y] = MV_NONE;
3264 void RemoveMovingField(int x, int y)
3266 int oldx = x, oldy = y, newx = x, newy = y;
3267 int element = Feld[x][y];
3268 int next_element = EL_UNDEFINED;
3270 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3273 if (IS_MOVING(x, y))
3275 Moving2Blocked(x, y, &newx, &newy);
3277 if (Feld[newx][newy] != EL_BLOCKED)
3279 /* element is moving, but target field is not free (blocked), but
3280 already occupied by something different (example: acid pool);
3281 in this case, only remove the moving field, but not the target */
3283 RemoveField(oldx, oldy);
3285 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3287 DrawLevelField(oldx, oldy);
3292 else if (element == EL_BLOCKED)
3294 Blocked2Moving(x, y, &oldx, &oldy);
3295 if (!IS_MOVING(oldx, oldy))
3299 if (element == EL_BLOCKED &&
3300 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3301 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3302 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3303 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3304 next_element = get_next_element(Feld[oldx][oldy]);
3306 RemoveField(oldx, oldy);
3307 RemoveField(newx, newy);
3309 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3311 if (next_element != EL_UNDEFINED)
3312 Feld[oldx][oldy] = next_element;
3314 DrawLevelField(oldx, oldy);
3315 DrawLevelField(newx, newy);
3318 void DrawDynamite(int x, int y)
3320 int sx = SCREENX(x), sy = SCREENY(y);
3321 int graphic = el2img(Feld[x][y]);
3324 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3327 if (IS_WALKABLE_INSIDE(Back[x][y]))
3331 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3332 else if (Store[x][y])
3333 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3335 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3337 if (Back[x][y] || Store[x][y])
3338 DrawGraphicThruMask(sx, sy, graphic, frame);
3340 DrawGraphic(sx, sy, graphic, frame);
3343 void CheckDynamite(int x, int y)
3345 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3349 if (MovDelay[x][y] != 0)
3352 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3358 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3365 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3367 boolean num_checked_players = 0;
3370 for (i = 0; i < MAX_PLAYERS; i++)
3372 if (stored_player[i].active)
3374 int sx = stored_player[i].jx;
3375 int sy = stored_player[i].jy;
3377 if (num_checked_players == 0)
3384 *sx1 = MIN(*sx1, sx);
3385 *sy1 = MIN(*sy1, sy);
3386 *sx2 = MAX(*sx2, sx);
3387 *sy2 = MAX(*sy2, sy);
3390 num_checked_players++;
3395 static boolean checkIfAllPlayersFitToScreen_RND()
3397 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3399 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3401 return (sx2 - sx1 < SCR_FIELDX &&
3402 sy2 - sy1 < SCR_FIELDY);
3405 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3407 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3409 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3411 *sx = (sx1 + sx2) / 2;
3412 *sy = (sy1 + sy2) / 2;
3416 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3417 int center_x, int center_y)
3419 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3421 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3423 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3424 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3427 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3431 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3433 return (max_dx <= SCR_FIELDX / 2 &&
3434 max_dy <= SCR_FIELDY / 2);
3442 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3443 boolean quick_relocation)
3445 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3446 boolean no_delay = (tape.warp_forward);
3447 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3448 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3450 if (quick_relocation)
3452 int offset = (setup.scroll_delay ? 3 : 0);
3459 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3461 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3462 x > SBX_Right + MIDPOSX ? SBX_Right :
3465 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3466 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3471 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3472 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3473 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3475 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3476 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3477 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3479 /* don't scroll over playfield boundaries */
3480 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3481 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3483 /* don't scroll over playfield boundaries */
3484 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3485 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3488 RedrawPlayfield(TRUE, 0,0,0,0);
3492 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3493 x > SBX_Right + MIDPOSX ? SBX_Right :
3496 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3497 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3500 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3502 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3505 int fx = FX, fy = FY;
3507 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3508 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3510 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3516 fx += dx * TILEX / 2;
3517 fy += dy * TILEY / 2;
3519 ScrollLevel(dx, dy);
3522 /* scroll in two steps of half tile size to make things smoother */
3523 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3525 Delay(wait_delay_value);
3527 /* scroll second step to align at full tile size */
3529 Delay(wait_delay_value);
3534 Delay(wait_delay_value);
3540 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3542 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3543 boolean no_delay = (tape.warp_forward);
3544 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3545 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3546 int jx = player->jx;
3547 int jy = player->jy;
3549 if (quick_relocation)
3551 int offset = (setup.scroll_delay ? 3 : 0);
3553 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3555 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3556 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3557 player->jx - MIDPOSX);
3559 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3560 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3561 player->jy - MIDPOSY);
3565 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3566 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3567 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3569 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3570 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3571 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3573 /* don't scroll over playfield boundaries */
3574 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3575 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3577 /* don't scroll over playfield boundaries */
3578 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3579 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3582 RedrawPlayfield(TRUE, 0,0,0,0);
3586 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3587 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3588 player->jx - MIDPOSX);
3590 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3591 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3592 player->jy - MIDPOSY);
3594 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3596 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3599 int fx = FX, fy = FY;
3601 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3602 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3604 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3610 fx += dx * TILEX / 2;
3611 fy += dy * TILEY / 2;
3613 ScrollLevel(dx, dy);
3616 /* scroll in two steps of half tile size to make things smoother */
3617 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3619 Delay(wait_delay_value);
3621 /* scroll second step to align at full tile size */
3623 Delay(wait_delay_value);
3628 Delay(wait_delay_value);
3634 void RelocatePlayer(int jx, int jy, int el_player_raw)
3636 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3637 int player_nr = GET_PLAYER_NR(el_player);
3638 struct PlayerInfo *player = &stored_player[player_nr];
3639 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3640 boolean no_delay = (tape.warp_forward);
3641 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3642 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3643 int old_jx = player->jx;
3644 int old_jy = player->jy;
3645 int old_element = Feld[old_jx][old_jy];
3646 int element = Feld[jx][jy];
3647 boolean player_relocated = (old_jx != jx || old_jy != jy);
3649 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3650 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3651 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3652 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3653 int leave_side_horiz = move_dir_horiz;
3654 int leave_side_vert = move_dir_vert;
3655 int enter_side = enter_side_horiz | enter_side_vert;
3656 int leave_side = leave_side_horiz | leave_side_vert;
3658 if (player->GameOver) /* do not reanimate dead player */
3661 if (!player_relocated) /* no need to relocate the player */
3664 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3666 RemoveField(jx, jy); /* temporarily remove newly placed player */
3667 DrawLevelField(jx, jy);
3670 if (player->present)
3672 while (player->MovPos)
3674 ScrollPlayer(player, SCROLL_GO_ON);
3675 ScrollScreen(NULL, SCROLL_GO_ON);
3677 AdvanceFrameAndPlayerCounters(player->index_nr);
3682 Delay(wait_delay_value);
3685 DrawPlayer(player); /* needed here only to cleanup last field */
3686 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3688 player->is_moving = FALSE;
3691 if (IS_CUSTOM_ELEMENT(old_element))
3692 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3694 player->index_bit, leave_side);
3696 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3698 player->index_bit, leave_side);
3700 Feld[jx][jy] = el_player;
3701 InitPlayerField(jx, jy, el_player, TRUE);
3703 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3705 Feld[jx][jy] = element;
3706 InitField(jx, jy, FALSE);
3710 /* only visually relocate centered player */
3712 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3713 level.instant_relocation);
3715 if (player->index_nr == game.centered_player_nr)
3716 DrawRelocatePlayer(player, level.instant_relocation);
3719 if (player == local_player) /* only visually relocate local player */
3720 DrawRelocatePlayer(player, level.instant_relocation);
3723 TestIfPlayerTouchesBadThing(jx, jy);
3724 TestIfPlayerTouchesCustomElement(jx, jy);
3726 if (IS_CUSTOM_ELEMENT(element))
3727 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3728 player->index_bit, enter_side);
3730 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3731 player->index_bit, enter_side);
3734 void Explode(int ex, int ey, int phase, int mode)
3740 /* !!! eliminate this variable !!! */
3741 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3743 if (game.explosions_delayed)
3745 ExplodeField[ex][ey] = mode;
3749 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3751 int center_element = Feld[ex][ey];
3752 int artwork_element, explosion_element; /* set these values later */
3755 /* --- This is only really needed (and now handled) in "Impact()". --- */
3756 /* do not explode moving elements that left the explode field in time */
3757 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3758 center_element == EL_EMPTY &&
3759 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3764 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3765 if (mode == EX_TYPE_NORMAL ||
3766 mode == EX_TYPE_CENTER ||
3767 mode == EX_TYPE_CROSS)
3768 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3771 /* remove things displayed in background while burning dynamite */
3772 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3775 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3777 /* put moving element to center field (and let it explode there) */
3778 center_element = MovingOrBlocked2Element(ex, ey);
3779 RemoveMovingField(ex, ey);
3780 Feld[ex][ey] = center_element;
3783 /* now "center_element" is finally determined -- set related values now */
3784 artwork_element = center_element; /* for custom player artwork */
3785 explosion_element = center_element; /* for custom player artwork */
3787 if (IS_PLAYER(ex, ey))
3789 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3791 artwork_element = stored_player[player_nr].artwork_element;
3793 if (level.use_explosion_element[player_nr])
3795 explosion_element = level.explosion_element[player_nr];
3796 artwork_element = explosion_element;
3801 if (mode == EX_TYPE_NORMAL ||
3802 mode == EX_TYPE_CENTER ||
3803 mode == EX_TYPE_CROSS)
3804 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3808 last_phase = element_info[explosion_element].explosion_delay + 1;
3810 last_phase = element_info[center_element].explosion_delay + 1;
3813 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3815 int xx = x - ex + 1;
3816 int yy = y - ey + 1;
3819 if (!IN_LEV_FIELD(x, y) ||
3820 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3821 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3824 element = Feld[x][y];
3826 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3828 element = MovingOrBlocked2Element(x, y);
3830 if (!IS_EXPLOSION_PROOF(element))
3831 RemoveMovingField(x, y);
3834 /* indestructible elements can only explode in center (but not flames) */
3835 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3836 mode == EX_TYPE_BORDER)) ||
3837 element == EL_FLAMES)
3840 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3841 behaviour, for example when touching a yamyam that explodes to rocks
3842 with active deadly shield, a rock is created under the player !!! */
3843 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3845 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3846 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3847 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3849 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3852 if (IS_ACTIVE_BOMB(element))
3854 /* re-activate things under the bomb like gate or penguin */
3855 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3862 /* save walkable background elements while explosion on same tile */
3863 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3864 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3865 Back[x][y] = element;
3867 /* ignite explodable elements reached by other explosion */
3868 if (element == EL_EXPLOSION)
3869 element = Store2[x][y];
3871 if (AmoebaNr[x][y] &&
3872 (element == EL_AMOEBA_FULL ||
3873 element == EL_BD_AMOEBA ||
3874 element == EL_AMOEBA_GROWING))
3876 AmoebaCnt[AmoebaNr[x][y]]--;
3877 AmoebaCnt2[AmoebaNr[x][y]]--;
3882 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3885 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3887 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3889 switch(StorePlayer[ex][ey])
3892 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3895 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3898 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3902 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3907 if (PLAYERINFO(ex, ey)->use_murphy)
3908 Store[x][y] = EL_EMPTY;
3911 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3912 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3913 else if (ELEM_IS_PLAYER(center_element))
3914 Store[x][y] = EL_EMPTY;
3915 else if (center_element == EL_YAMYAM)
3916 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3917 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3918 Store[x][y] = element_info[center_element].content.e[xx][yy];
3920 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3921 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3922 otherwise) -- FIX THIS !!! */
3923 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3924 Store[x][y] = element_info[element].content.e[1][1];
3926 else if (!CAN_EXPLODE(element))
3927 Store[x][y] = element_info[element].content.e[1][1];
3930 Store[x][y] = EL_EMPTY;
3932 else if (center_element == EL_MOLE)
3933 Store[x][y] = EL_EMERALD_RED;
3934 else if (center_element == EL_PENGUIN)
3935 Store[x][y] = EL_EMERALD_PURPLE;
3936 else if (center_element == EL_BUG)
3937 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3938 else if (center_element == EL_BD_BUTTERFLY)
3939 Store[x][y] = EL_BD_DIAMOND;
3940 else if (center_element == EL_SP_ELECTRON)
3941 Store[x][y] = EL_SP_INFOTRON;
3942 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3943 Store[x][y] = level.amoeba_content;
3944 else if (center_element == EL_YAMYAM)
3945 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3946 else if (IS_CUSTOM_ELEMENT(center_element) &&
3947 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3948 Store[x][y] = element_info[center_element].content.e[xx][yy];
3949 else if (element == EL_WALL_EMERALD)
3950 Store[x][y] = EL_EMERALD;
3951 else if (element == EL_WALL_DIAMOND)
3952 Store[x][y] = EL_DIAMOND;
3953 else if (element == EL_WALL_BD_DIAMOND)
3954 Store[x][y] = EL_BD_DIAMOND;
3955 else if (element == EL_WALL_EMERALD_YELLOW)
3956 Store[x][y] = EL_EMERALD_YELLOW;
3957 else if (element == EL_WALL_EMERALD_RED)
3958 Store[x][y] = EL_EMERALD_RED;
3959 else if (element == EL_WALL_EMERALD_PURPLE)
3960 Store[x][y] = EL_EMERALD_PURPLE;
3961 else if (element == EL_WALL_PEARL)
3962 Store[x][y] = EL_PEARL;
3963 else if (element == EL_WALL_CRYSTAL)
3964 Store[x][y] = EL_CRYSTAL;
3965 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3966 Store[x][y] = element_info[element].content.e[1][1];
3968 Store[x][y] = EL_EMPTY;
3971 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3972 center_element == EL_AMOEBA_TO_DIAMOND)
3973 Store2[x][y] = element;
3975 Feld[x][y] = EL_EXPLOSION;
3976 GfxElement[x][y] = artwork_element;
3979 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3980 x, y, artwork_element, EL_NAME(artwork_element));
3983 ExplodePhase[x][y] = 1;
3984 ExplodeDelay[x][y] = last_phase;
3989 if (center_element == EL_YAMYAM)
3990 game.yamyam_content_nr =
3991 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4003 GfxFrame[x][y] = 0; /* restart explosion animation */
4005 last_phase = ExplodeDelay[x][y];
4007 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4011 /* activate this even in non-DEBUG version until cause for crash in
4012 getGraphicAnimationFrame() (see below) is found and eliminated */
4018 /* this can happen if the player leaves an explosion just in time */
4019 if (GfxElement[x][y] == EL_UNDEFINED)
4020 GfxElement[x][y] = EL_EMPTY;
4022 if (GfxElement[x][y] == EL_UNDEFINED)
4025 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4026 printf("Explode(): This should never happen!\n");
4029 GfxElement[x][y] = EL_EMPTY;
4035 border_element = Store2[x][y];
4036 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4037 border_element = StorePlayer[x][y];
4039 if (phase == element_info[border_element].ignition_delay ||
4040 phase == last_phase)
4042 boolean border_explosion = FALSE;
4044 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4045 !PLAYER_EXPLOSION_PROTECTED(x, y))
4047 KillPlayerUnlessExplosionProtected(x, y);
4048 border_explosion = TRUE;
4050 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4052 Feld[x][y] = Store2[x][y];
4055 border_explosion = TRUE;
4057 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4059 AmoebeUmwandeln(x, y);
4061 border_explosion = TRUE;
4064 /* if an element just explodes due to another explosion (chain-reaction),
4065 do not immediately end the new explosion when it was the last frame of
4066 the explosion (as it would be done in the following "if"-statement!) */
4067 if (border_explosion && phase == last_phase)
4071 if (phase == last_phase)
4075 element = Feld[x][y] = Store[x][y];
4076 Store[x][y] = Store2[x][y] = 0;
4077 GfxElement[x][y] = EL_UNDEFINED;
4079 /* player can escape from explosions and might therefore be still alive */
4080 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4081 element <= EL_PLAYER_IS_EXPLODING_4)
4083 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4084 int explosion_element = EL_PLAYER_1 + player_nr;
4085 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4086 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4088 if (level.use_explosion_element[player_nr])
4089 explosion_element = level.explosion_element[player_nr];
4091 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4092 element_info[explosion_element].content.e[xx][yy]);
4095 /* restore probably existing indestructible background element */
4096 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4097 element = Feld[x][y] = Back[x][y];
4100 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4101 GfxDir[x][y] = MV_NONE;
4102 ChangeDelay[x][y] = 0;
4103 ChangePage[x][y] = -1;
4105 #if USE_NEW_CUSTOM_VALUE
4106 CustomValue[x][y] = 0;
4109 InitField_WithBug2(x, y, FALSE);
4111 DrawLevelField(x, y);
4113 TestIfElementTouchesCustomElement(x, y);
4115 if (GFX_CRUMBLED(element))
4116 DrawLevelFieldCrumbledSandNeighbours(x, y);
4118 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4119 StorePlayer[x][y] = 0;
4121 if (ELEM_IS_PLAYER(element))
4122 RelocatePlayer(x, y, element);
4124 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4126 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4127 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4130 DrawLevelFieldCrumbledSand(x, y);
4132 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4134 DrawLevelElement(x, y, Back[x][y]);
4135 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4137 else if (IS_WALKABLE_UNDER(Back[x][y]))
4139 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4140 DrawLevelElementThruMask(x, y, Back[x][y]);
4142 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4143 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4147 void DynaExplode(int ex, int ey)
4150 int dynabomb_element = Feld[ex][ey];
4151 int dynabomb_size = 1;
4152 boolean dynabomb_xl = FALSE;
4153 struct PlayerInfo *player;
4154 static int xy[4][2] =
4162 if (IS_ACTIVE_BOMB(dynabomb_element))
4164 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4165 dynabomb_size = player->dynabomb_size;
4166 dynabomb_xl = player->dynabomb_xl;
4167 player->dynabombs_left++;
4170 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4172 for (i = 0; i < NUM_DIRECTIONS; i++)
4174 for (j = 1; j <= dynabomb_size; j++)
4176 int x = ex + j * xy[i][0];
4177 int y = ey + j * xy[i][1];
4180 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4183 element = Feld[x][y];
4185 /* do not restart explosions of fields with active bombs */
4186 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4189 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4191 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4192 !IS_DIGGABLE(element) && !dynabomb_xl)
4198 void Bang(int x, int y)
4200 int element = MovingOrBlocked2Element(x, y);
4201 int explosion_type = EX_TYPE_NORMAL;
4203 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4205 struct PlayerInfo *player = PLAYERINFO(x, y);
4207 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4208 player->element_nr);
4210 if (level.use_explosion_element[player->index_nr])
4212 int explosion_element = level.explosion_element[player->index_nr];
4214 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4215 explosion_type = EX_TYPE_CROSS;
4216 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4217 explosion_type = EX_TYPE_CENTER;
4225 case EL_BD_BUTTERFLY:
4228 case EL_DARK_YAMYAM:
4232 RaiseScoreElement(element);
4235 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4236 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4237 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4238 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4239 case EL_DYNABOMB_INCREASE_NUMBER:
4240 case EL_DYNABOMB_INCREASE_SIZE:
4241 case EL_DYNABOMB_INCREASE_POWER:
4242 explosion_type = EX_TYPE_DYNA;
4247 case EL_LAMP_ACTIVE:
4248 case EL_AMOEBA_TO_DIAMOND:
4249 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4250 explosion_type = EX_TYPE_CENTER;
4254 if (element_info[element].explosion_type == EXPLODES_CROSS)
4255 explosion_type = EX_TYPE_CROSS;
4256 else if (element_info[element].explosion_type == EXPLODES_1X1)
4257 explosion_type = EX_TYPE_CENTER;
4261 if (explosion_type == EX_TYPE_DYNA)
4264 Explode(x, y, EX_PHASE_START, explosion_type);
4266 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4269 void SplashAcid(int x, int y)
4271 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4272 (!IN_LEV_FIELD(x - 1, y - 2) ||
4273 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4274 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4276 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4277 (!IN_LEV_FIELD(x + 1, y - 2) ||
4278 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4279 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4281 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4284 static void InitBeltMovement()
4286 static int belt_base_element[4] =
4288 EL_CONVEYOR_BELT_1_LEFT,
4289 EL_CONVEYOR_BELT_2_LEFT,
4290 EL_CONVEYOR_BELT_3_LEFT,
4291 EL_CONVEYOR_BELT_4_LEFT
4293 static int belt_base_active_element[4] =
4295 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4296 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4297 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4298 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4303 /* set frame order for belt animation graphic according to belt direction */
4304 for (i = 0; i < NUM_BELTS; i++)
4308 for (j = 0; j < NUM_BELT_PARTS; j++)
4310 int element = belt_base_active_element[belt_nr] + j;
4311 int graphic = el2img(element);
4313 if (game.belt_dir[i] == MV_LEFT)
4314 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4316 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4321 SCAN_PLAYFIELD(x, y)
4323 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4326 int element = Feld[x][y];
4328 for (i = 0; i < NUM_BELTS; i++)
4330 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4332 int e_belt_nr = getBeltNrFromBeltElement(element);
4335 if (e_belt_nr == belt_nr)
4337 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4339 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4346 static void ToggleBeltSwitch(int x, int y)
4348 static int belt_base_element[4] =
4350 EL_CONVEYOR_BELT_1_LEFT,
4351 EL_CONVEYOR_BELT_2_LEFT,
4352 EL_CONVEYOR_BELT_3_LEFT,
4353 EL_CONVEYOR_BELT_4_LEFT
4355 static int belt_base_active_element[4] =
4357 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4358 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4359 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4360 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4362 static int belt_base_switch_element[4] =
4364 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4365 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4366 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4367 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4369 static int belt_move_dir[4] =
4377 int element = Feld[x][y];
4378 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4379 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4380 int belt_dir = belt_move_dir[belt_dir_nr];
4383 if (!IS_BELT_SWITCH(element))
4386 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4387 game.belt_dir[belt_nr] = belt_dir;
4389 if (belt_dir_nr == 3)
4392 /* set frame order for belt animation graphic according to belt direction */
4393 for (i = 0; i < NUM_BELT_PARTS; i++)
4395 int element = belt_base_active_element[belt_nr] + i;
4396 int graphic = el2img(element);
4398 if (belt_dir == MV_LEFT)
4399 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4401 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4405 SCAN_PLAYFIELD(xx, yy)
4407 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4410 int element = Feld[xx][yy];
4412 if (IS_BELT_SWITCH(element))
4414 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4416 if (e_belt_nr == belt_nr)
4418 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4419 DrawLevelField(xx, yy);
4422 else if (IS_BELT(element) && belt_dir != MV_NONE)
4424 int e_belt_nr = getBeltNrFromBeltElement(element);
4426 if (e_belt_nr == belt_nr)
4428 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4430 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4431 DrawLevelField(xx, yy);
4434 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4436 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4438 if (e_belt_nr == belt_nr)
4440 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4442 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4443 DrawLevelField(xx, yy);
4449 static void ToggleSwitchgateSwitch(int x, int y)
4453 game.switchgate_pos = !game.switchgate_pos;
4456 SCAN_PLAYFIELD(xx, yy)
4458 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4461 int element = Feld[xx][yy];
4463 #if !USE_BOTH_SWITCHGATE_SWITCHES
4464 if (element == EL_SWITCHGATE_SWITCH_UP ||
4465 element == EL_SWITCHGATE_SWITCH_DOWN)
4467 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4468 DrawLevelField(xx, yy);
4471 if (element == EL_SWITCHGATE_SWITCH_UP)
4473 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4474 DrawLevelField(xx, yy);
4476 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4478 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4479 DrawLevelField(xx, yy);
4482 else if (element == EL_SWITCHGATE_OPEN ||
4483 element == EL_SWITCHGATE_OPENING)
4485 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4487 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4489 else if (element == EL_SWITCHGATE_CLOSED ||
4490 element == EL_SWITCHGATE_CLOSING)
4492 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4494 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4499 static int getInvisibleActiveFromInvisibleElement(int element)
4501 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4502 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4503 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4507 static int getInvisibleFromInvisibleActiveElement(int element)
4509 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4510 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4511 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4515 static void RedrawAllLightSwitchesAndInvisibleElements()
4520 SCAN_PLAYFIELD(x, y)
4522 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4525 int element = Feld[x][y];
4527 if (element == EL_LIGHT_SWITCH &&
4528 game.light_time_left > 0)
4530 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4531 DrawLevelField(x, y);
4533 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4534 game.light_time_left == 0)
4536 Feld[x][y] = EL_LIGHT_SWITCH;
4537 DrawLevelField(x, y);
4539 else if (element == EL_EMC_DRIPPER &&
4540 game.light_time_left > 0)
4542 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4543 DrawLevelField(x, y);
4545 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4546 game.light_time_left == 0)
4548 Feld[x][y] = EL_EMC_DRIPPER;
4549 DrawLevelField(x, y);
4551 else if (element == EL_INVISIBLE_STEELWALL ||
4552 element == EL_INVISIBLE_WALL ||
4553 element == EL_INVISIBLE_SAND)
4555 if (game.light_time_left > 0)
4556 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4558 DrawLevelField(x, y);
4560 /* uncrumble neighbour fields, if needed */
4561 if (element == EL_INVISIBLE_SAND)
4562 DrawLevelFieldCrumbledSandNeighbours(x, y);
4564 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4565 element == EL_INVISIBLE_WALL_ACTIVE ||
4566 element == EL_INVISIBLE_SAND_ACTIVE)
4568 if (game.light_time_left == 0)
4569 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4571 DrawLevelField(x, y);
4573 /* re-crumble neighbour fields, if needed */
4574 if (element == EL_INVISIBLE_SAND)
4575 DrawLevelFieldCrumbledSandNeighbours(x, y);
4580 static void RedrawAllInvisibleElementsForLenses()
4585 SCAN_PLAYFIELD(x, y)
4587 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4590 int element = Feld[x][y];
4592 if (element == EL_EMC_DRIPPER &&
4593 game.lenses_time_left > 0)
4595 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4596 DrawLevelField(x, y);
4598 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4599 game.lenses_time_left == 0)
4601 Feld[x][y] = EL_EMC_DRIPPER;
4602 DrawLevelField(x, y);
4604 else if (element == EL_INVISIBLE_STEELWALL ||
4605 element == EL_INVISIBLE_WALL ||
4606 element == EL_INVISIBLE_SAND)
4608 if (game.lenses_time_left > 0)
4609 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4611 DrawLevelField(x, y);
4613 /* uncrumble neighbour fields, if needed */
4614 if (element == EL_INVISIBLE_SAND)
4615 DrawLevelFieldCrumbledSandNeighbours(x, y);
4617 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4618 element == EL_INVISIBLE_WALL_ACTIVE ||
4619 element == EL_INVISIBLE_SAND_ACTIVE)
4621 if (game.lenses_time_left == 0)
4622 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4624 DrawLevelField(x, y);
4626 /* re-crumble neighbour fields, if needed */
4627 if (element == EL_INVISIBLE_SAND)
4628 DrawLevelFieldCrumbledSandNeighbours(x, y);
4633 static void RedrawAllInvisibleElementsForMagnifier()
4638 SCAN_PLAYFIELD(x, y)
4640 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4643 int element = Feld[x][y];
4645 if (element == EL_EMC_FAKE_GRASS &&
4646 game.magnify_time_left > 0)
4648 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4649 DrawLevelField(x, y);
4651 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4652 game.magnify_time_left == 0)
4654 Feld[x][y] = EL_EMC_FAKE_GRASS;
4655 DrawLevelField(x, y);
4657 else if (IS_GATE_GRAY(element) &&
4658 game.magnify_time_left > 0)
4660 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4661 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4662 IS_EM_GATE_GRAY(element) ?
4663 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4664 IS_EMC_GATE_GRAY(element) ?
4665 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4667 DrawLevelField(x, y);
4669 else if (IS_GATE_GRAY_ACTIVE(element) &&
4670 game.magnify_time_left == 0)
4672 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4673 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4674 IS_EM_GATE_GRAY_ACTIVE(element) ?
4675 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4676 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4677 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4679 DrawLevelField(x, y);
4684 static void ToggleLightSwitch(int x, int y)
4686 int element = Feld[x][y];
4688 game.light_time_left =
4689 (element == EL_LIGHT_SWITCH ?
4690 level.time_light * FRAMES_PER_SECOND : 0);
4692 RedrawAllLightSwitchesAndInvisibleElements();
4695 static void ActivateTimegateSwitch(int x, int y)
4699 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4702 SCAN_PLAYFIELD(xx, yy)
4704 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4707 int element = Feld[xx][yy];
4709 if (element == EL_TIMEGATE_CLOSED ||
4710 element == EL_TIMEGATE_CLOSING)
4712 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4713 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4717 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4719 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4720 DrawLevelField(xx, yy);
4726 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4729 void Impact(int x, int y)
4731 boolean last_line = (y == lev_fieldy - 1);
4732 boolean object_hit = FALSE;
4733 boolean impact = (last_line || object_hit);
4734 int element = Feld[x][y];
4735 int smashed = EL_STEELWALL;
4737 if (!last_line) /* check if element below was hit */
4739 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4742 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4743 MovDir[x][y + 1] != MV_DOWN ||
4744 MovPos[x][y + 1] <= TILEY / 2));
4746 /* do not smash moving elements that left the smashed field in time */
4747 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4748 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4751 #if USE_QUICKSAND_IMPACT_BUGFIX
4752 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4754 RemoveMovingField(x, y + 1);
4755 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4756 Feld[x][y + 2] = EL_ROCK;
4757 DrawLevelField(x, y + 2);
4764 smashed = MovingOrBlocked2Element(x, y + 1);
4766 impact = (last_line || object_hit);
4769 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4771 SplashAcid(x, y + 1);
4775 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4776 /* only reset graphic animation if graphic really changes after impact */
4778 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4780 ResetGfxAnimation(x, y);
4781 DrawLevelField(x, y);
4784 if (impact && CAN_EXPLODE_IMPACT(element))
4789 else if (impact && element == EL_PEARL)
4791 ResetGfxAnimation(x, y);
4793 Feld[x][y] = EL_PEARL_BREAKING;
4794 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4797 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4799 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4804 if (impact && element == EL_AMOEBA_DROP)
4806 if (object_hit && IS_PLAYER(x, y + 1))
4807 KillPlayerUnlessEnemyProtected(x, y + 1);
4808 else if (object_hit && smashed == EL_PENGUIN)
4812 Feld[x][y] = EL_AMOEBA_GROWING;
4813 Store[x][y] = EL_AMOEBA_WET;
4815 ResetRandomAnimationValue(x, y);
4820 if (object_hit) /* check which object was hit */
4822 if (CAN_PASS_MAGIC_WALL(element) &&
4823 (smashed == EL_MAGIC_WALL ||
4824 smashed == EL_BD_MAGIC_WALL))
4827 int activated_magic_wall =
4828 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4829 EL_BD_MAGIC_WALL_ACTIVE);
4831 /* activate magic wall / mill */
4833 SCAN_PLAYFIELD(xx, yy)
4835 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4837 if (Feld[xx][yy] == smashed)
4838 Feld[xx][yy] = activated_magic_wall;
4840 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4841 game.magic_wall_active = TRUE;
4843 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4844 SND_MAGIC_WALL_ACTIVATING :
4845 SND_BD_MAGIC_WALL_ACTIVATING));
4848 if (IS_PLAYER(x, y + 1))
4850 if (CAN_SMASH_PLAYER(element))
4852 KillPlayerUnlessEnemyProtected(x, y + 1);
4856 else if (smashed == EL_PENGUIN)
4858 if (CAN_SMASH_PLAYER(element))
4864 else if (element == EL_BD_DIAMOND)
4866 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4872 else if (((element == EL_SP_INFOTRON ||
4873 element == EL_SP_ZONK) &&
4874 (smashed == EL_SP_SNIKSNAK ||
4875 smashed == EL_SP_ELECTRON ||
4876 smashed == EL_SP_DISK_ORANGE)) ||
4877 (element == EL_SP_INFOTRON &&
4878 smashed == EL_SP_DISK_YELLOW))
4883 else if (CAN_SMASH_EVERYTHING(element))
4885 if (IS_CLASSIC_ENEMY(smashed) ||
4886 CAN_EXPLODE_SMASHED(smashed))
4891 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4893 if (smashed == EL_LAMP ||
4894 smashed == EL_LAMP_ACTIVE)
4899 else if (smashed == EL_NUT)
4901 Feld[x][y + 1] = EL_NUT_BREAKING;
4902 PlayLevelSound(x, y, SND_NUT_BREAKING);
4903 RaiseScoreElement(EL_NUT);
4906 else if (smashed == EL_PEARL)
4908 ResetGfxAnimation(x, y);
4910 Feld[x][y + 1] = EL_PEARL_BREAKING;
4911 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4914 else if (smashed == EL_DIAMOND)
4916 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4917 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4920 else if (IS_BELT_SWITCH(smashed))
4922 ToggleBeltSwitch(x, y + 1);
4924 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4925 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4927 ToggleSwitchgateSwitch(x, y + 1);
4929 else if (smashed == EL_LIGHT_SWITCH ||
4930 smashed == EL_LIGHT_SWITCH_ACTIVE)
4932 ToggleLightSwitch(x, y + 1);
4937 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4940 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4942 CheckElementChangeBySide(x, y + 1, smashed, element,
4943 CE_SWITCHED, CH_SIDE_TOP);
4944 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4950 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4955 /* play sound of magic wall / mill */
4957 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4958 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4960 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4961 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4962 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4963 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4968 /* play sound of object that hits the ground */
4969 if (last_line || object_hit)
4970 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4973 inline static void TurnRoundExt(int x, int y)
4985 { 0, 0 }, { 0, 0 }, { 0, 0 },
4990 int left, right, back;
4994 { MV_DOWN, MV_UP, MV_RIGHT },
4995 { MV_UP, MV_DOWN, MV_LEFT },
4997 { MV_LEFT, MV_RIGHT, MV_DOWN },
5001 { MV_RIGHT, MV_LEFT, MV_UP }
5004 int element = Feld[x][y];
5005 int move_pattern = element_info[element].move_pattern;
5007 int old_move_dir = MovDir[x][y];
5008 int left_dir = turn[old_move_dir].left;
5009 int right_dir = turn[old_move_dir].right;
5010 int back_dir = turn[old_move_dir].back;
5012 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5013 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5014 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5015 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5017 int left_x = x + left_dx, left_y = y + left_dy;
5018 int right_x = x + right_dx, right_y = y + right_dy;
5019 int move_x = x + move_dx, move_y = y + move_dy;
5023 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5025 TestIfBadThingTouchesOtherBadThing(x, y);
5027 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5028 MovDir[x][y] = right_dir;
5029 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5030 MovDir[x][y] = left_dir;
5032 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5034 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5037 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5039 TestIfBadThingTouchesOtherBadThing(x, y);
5041 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5042 MovDir[x][y] = left_dir;
5043 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5044 MovDir[x][y] = right_dir;
5046 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5048 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5051 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5053 TestIfBadThingTouchesOtherBadThing(x, y);
5055 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5056 MovDir[x][y] = left_dir;
5057 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5058 MovDir[x][y] = right_dir;
5060 if (MovDir[x][y] != old_move_dir)
5063 else if (element == EL_YAMYAM)
5065 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5066 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5068 if (can_turn_left && can_turn_right)
5069 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5070 else if (can_turn_left)
5071 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5072 else if (can_turn_right)
5073 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5075 MovDir[x][y] = back_dir;
5077 MovDelay[x][y] = 16 + 16 * RND(3);
5079 else if (element == EL_DARK_YAMYAM)
5081 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5083 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5086 if (can_turn_left && can_turn_right)
5087 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5088 else if (can_turn_left)
5089 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5090 else if (can_turn_right)
5091 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5093 MovDir[x][y] = back_dir;
5095 MovDelay[x][y] = 16 + 16 * RND(3);
5097 else if (element == EL_PACMAN)
5099 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5100 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5102 if (can_turn_left && can_turn_right)
5103 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5104 else if (can_turn_left)
5105 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5106 else if (can_turn_right)
5107 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5109 MovDir[x][y] = back_dir;
5111 MovDelay[x][y] = 6 + RND(40);
5113 else if (element == EL_PIG)
5115 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5116 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5117 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5118 boolean should_turn_left, should_turn_right, should_move_on;
5120 int rnd = RND(rnd_value);
5122 should_turn_left = (can_turn_left &&
5124 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5125 y + back_dy + left_dy)));
5126 should_turn_right = (can_turn_right &&
5128 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5129 y + back_dy + right_dy)));
5130 should_move_on = (can_move_on &&
5133 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5134 y + move_dy + left_dy) ||
5135 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5136 y + move_dy + right_dy)));
5138 if (should_turn_left || should_turn_right || should_move_on)
5140 if (should_turn_left && should_turn_right && should_move_on)
5141 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5142 rnd < 2 * rnd_value / 3 ? right_dir :
5144 else if (should_turn_left && should_turn_right)
5145 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5146 else if (should_turn_left && should_move_on)
5147 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5148 else if (should_turn_right && should_move_on)
5149 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5150 else if (should_turn_left)
5151 MovDir[x][y] = left_dir;
5152 else if (should_turn_right)
5153 MovDir[x][y] = right_dir;
5154 else if (should_move_on)
5155 MovDir[x][y] = old_move_dir;
5157 else if (can_move_on && rnd > rnd_value / 8)
5158 MovDir[x][y] = old_move_dir;
5159 else if (can_turn_left && can_turn_right)
5160 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5161 else if (can_turn_left && rnd > rnd_value / 8)
5162 MovDir[x][y] = left_dir;
5163 else if (can_turn_right && rnd > rnd_value/8)
5164 MovDir[x][y] = right_dir;
5166 MovDir[x][y] = back_dir;
5168 xx = x + move_xy[MovDir[x][y]].dx;
5169 yy = y + move_xy[MovDir[x][y]].dy;
5171 if (!IN_LEV_FIELD(xx, yy) ||
5172 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5173 MovDir[x][y] = old_move_dir;
5177 else if (element == EL_DRAGON)
5179 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5180 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5181 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5183 int rnd = RND(rnd_value);
5185 if (can_move_on && rnd > rnd_value / 8)
5186 MovDir[x][y] = old_move_dir;
5187 else if (can_turn_left && can_turn_right)
5188 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5189 else if (can_turn_left && rnd > rnd_value / 8)
5190 MovDir[x][y] = left_dir;
5191 else if (can_turn_right && rnd > rnd_value / 8)
5192 MovDir[x][y] = right_dir;
5194 MovDir[x][y] = back_dir;
5196 xx = x + move_xy[MovDir[x][y]].dx;
5197 yy = y + move_xy[MovDir[x][y]].dy;
5199 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5200 MovDir[x][y] = old_move_dir;
5204 else if (element == EL_MOLE)
5206 boolean can_move_on =
5207 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5208 IS_AMOEBOID(Feld[move_x][move_y]) ||
5209 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5212 boolean can_turn_left =
5213 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5214 IS_AMOEBOID(Feld[left_x][left_y])));
5216 boolean can_turn_right =
5217 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5218 IS_AMOEBOID(Feld[right_x][right_y])));
5220 if (can_turn_left && can_turn_right)
5221 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5222 else if (can_turn_left)
5223 MovDir[x][y] = left_dir;
5225 MovDir[x][y] = right_dir;
5228 if (MovDir[x][y] != old_move_dir)
5231 else if (element == EL_BALLOON)
5233 MovDir[x][y] = game.wind_direction;
5236 else if (element == EL_SPRING)
5238 #if USE_NEW_SPRING_BUMPER
5239 if (MovDir[x][y] & MV_HORIZONTAL)
5241 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5242 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5244 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5245 ResetGfxAnimation(move_x, move_y);
5246 DrawLevelField(move_x, move_y);
5248 MovDir[x][y] = back_dir;
5250 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5251 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5252 MovDir[x][y] = MV_NONE;
5255 if (MovDir[x][y] & MV_HORIZONTAL &&
5256 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5257 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5258 MovDir[x][y] = MV_NONE;
5263 else if (element == EL_ROBOT ||
5264 element == EL_SATELLITE ||
5265 element == EL_PENGUIN ||
5266 element == EL_EMC_ANDROID)
5268 int attr_x = -1, attr_y = -1;
5279 for (i = 0; i < MAX_PLAYERS; i++)
5281 struct PlayerInfo *player = &stored_player[i];
5282 int jx = player->jx, jy = player->jy;
5284 if (!player->active)
5288 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5296 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5297 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5298 game.engine_version < VERSION_IDENT(3,1,0,0)))
5304 if (element == EL_PENGUIN)
5307 static int xy[4][2] =
5315 for (i = 0; i < NUM_DIRECTIONS; i++)
5317 int ex = x + xy[i][0];
5318 int ey = y + xy[i][1];
5320 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5329 MovDir[x][y] = MV_NONE;
5331 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5332 else if (attr_x > x)
5333 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5335 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5336 else if (attr_y > y)
5337 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5339 if (element == EL_ROBOT)
5343 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5344 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5345 Moving2Blocked(x, y, &newx, &newy);
5347 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5348 MovDelay[x][y] = 8 + 8 * !RND(3);
5350 MovDelay[x][y] = 16;
5352 else if (element == EL_PENGUIN)
5358 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5360 boolean first_horiz = RND(2);
5361 int new_move_dir = MovDir[x][y];
5364 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5365 Moving2Blocked(x, y, &newx, &newy);
5367 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5371 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5372 Moving2Blocked(x, y, &newx, &newy);
5374 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5377 MovDir[x][y] = old_move_dir;
5381 else if (element == EL_SATELLITE)
5387 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5389 boolean first_horiz = RND(2);
5390 int new_move_dir = MovDir[x][y];
5393 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5394 Moving2Blocked(x, y, &newx, &newy);
5396 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5400 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5401 Moving2Blocked(x, y, &newx, &newy);
5403 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5406 MovDir[x][y] = old_move_dir;
5410 else if (element == EL_EMC_ANDROID)
5412 static int check_pos[16] =
5414 -1, /* 0 => (invalid) */
5415 7, /* 1 => MV_LEFT */
5416 3, /* 2 => MV_RIGHT */
5417 -1, /* 3 => (invalid) */
5419 0, /* 5 => MV_LEFT | MV_UP */
5420 2, /* 6 => MV_RIGHT | MV_UP */
5421 -1, /* 7 => (invalid) */
5422 5, /* 8 => MV_DOWN */
5423 6, /* 9 => MV_LEFT | MV_DOWN */
5424 4, /* 10 => MV_RIGHT | MV_DOWN */
5425 -1, /* 11 => (invalid) */
5426 -1, /* 12 => (invalid) */
5427 -1, /* 13 => (invalid) */
5428 -1, /* 14 => (invalid) */
5429 -1, /* 15 => (invalid) */
5437 { -1, -1, MV_LEFT | MV_UP },
5439 { +1, -1, MV_RIGHT | MV_UP },
5440 { +1, 0, MV_RIGHT },
5441 { +1, +1, MV_RIGHT | MV_DOWN },
5443 { -1, +1, MV_LEFT | MV_DOWN },
5446 int start_pos, check_order;
5447 boolean can_clone = FALSE;
5450 /* check if there is any free field around current position */
5451 for (i = 0; i < 8; i++)
5453 int newx = x + check_xy[i].dx;
5454 int newy = y + check_xy[i].dy;
5456 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5464 if (can_clone) /* randomly find an element to clone */
5468 start_pos = check_pos[RND(8)];
5469 check_order = (RND(2) ? -1 : +1);
5471 for (i = 0; i < 8; i++)
5473 int pos_raw = start_pos + i * check_order;
5474 int pos = (pos_raw + 8) % 8;
5475 int newx = x + check_xy[pos].dx;
5476 int newy = y + check_xy[pos].dy;
5478 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5480 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5481 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5483 Store[x][y] = Feld[newx][newy];
5492 if (can_clone) /* randomly find a direction to move */
5496 start_pos = check_pos[RND(8)];
5497 check_order = (RND(2) ? -1 : +1);
5499 for (i = 0; i < 8; i++)
5501 int pos_raw = start_pos + i * check_order;
5502 int pos = (pos_raw + 8) % 8;
5503 int newx = x + check_xy[pos].dx;
5504 int newy = y + check_xy[pos].dy;
5505 int new_move_dir = check_xy[pos].dir;
5507 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5509 MovDir[x][y] = new_move_dir;
5510 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5519 if (can_clone) /* cloning and moving successful */
5522 /* cannot clone -- try to move towards player */
5524 start_pos = check_pos[MovDir[x][y] & 0x0f];
5525 check_order = (RND(2) ? -1 : +1);
5527 for (i = 0; i < 3; i++)
5529 /* first check start_pos, then previous/next or (next/previous) pos */
5530 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5531 int pos = (pos_raw + 8) % 8;
5532 int newx = x + check_xy[pos].dx;
5533 int newy = y + check_xy[pos].dy;
5534 int new_move_dir = check_xy[pos].dir;
5536 if (IS_PLAYER(newx, newy))
5539 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5541 MovDir[x][y] = new_move_dir;
5542 MovDelay[x][y] = level.android_move_time * 8 + 1;
5549 else if (move_pattern == MV_TURNING_LEFT ||
5550 move_pattern == MV_TURNING_RIGHT ||
5551 move_pattern == MV_TURNING_LEFT_RIGHT ||
5552 move_pattern == MV_TURNING_RIGHT_LEFT ||
5553 move_pattern == MV_TURNING_RANDOM ||
5554 move_pattern == MV_ALL_DIRECTIONS)
5556 boolean can_turn_left =
5557 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5558 boolean can_turn_right =
5559 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5561 if (element_info[element].move_stepsize == 0) /* "not moving" */
5564 if (move_pattern == MV_TURNING_LEFT)
5565 MovDir[x][y] = left_dir;
5566 else if (move_pattern == MV_TURNING_RIGHT)
5567 MovDir[x][y] = right_dir;
5568 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5569 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5570 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5571 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5572 else if (move_pattern == MV_TURNING_RANDOM)
5573 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5574 can_turn_right && !can_turn_left ? right_dir :
5575 RND(2) ? left_dir : right_dir);
5576 else if (can_turn_left && can_turn_right)
5577 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5578 else if (can_turn_left)
5579 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5580 else if (can_turn_right)
5581 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5583 MovDir[x][y] = back_dir;
5585 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5587 else if (move_pattern == MV_HORIZONTAL ||
5588 move_pattern == MV_VERTICAL)
5590 if (move_pattern & old_move_dir)
5591 MovDir[x][y] = back_dir;
5592 else if (move_pattern == MV_HORIZONTAL)
5593 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5594 else if (move_pattern == MV_VERTICAL)
5595 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5597 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5599 else if (move_pattern & MV_ANY_DIRECTION)
5601 MovDir[x][y] = move_pattern;
5602 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5604 else if (move_pattern & MV_WIND_DIRECTION)
5606 MovDir[x][y] = game.wind_direction;
5607 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5609 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5611 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5612 MovDir[x][y] = left_dir;
5613 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5614 MovDir[x][y] = right_dir;
5616 if (MovDir[x][y] != old_move_dir)
5617 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5619 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5621 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5622 MovDir[x][y] = right_dir;
5623 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5624 MovDir[x][y] = left_dir;
5626 if (MovDir[x][y] != old_move_dir)
5627 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5629 else if (move_pattern == MV_TOWARDS_PLAYER ||
5630 move_pattern == MV_AWAY_FROM_PLAYER)
5632 int attr_x = -1, attr_y = -1;
5634 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5645 for (i = 0; i < MAX_PLAYERS; i++)
5647 struct PlayerInfo *player = &stored_player[i];
5648 int jx = player->jx, jy = player->jy;
5650 if (!player->active)
5654 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5662 MovDir[x][y] = MV_NONE;
5664 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5665 else if (attr_x > x)
5666 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5668 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5669 else if (attr_y > y)
5670 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5672 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5674 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5676 boolean first_horiz = RND(2);
5677 int new_move_dir = MovDir[x][y];
5679 if (element_info[element].move_stepsize == 0) /* "not moving" */
5681 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5682 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5688 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5689 Moving2Blocked(x, y, &newx, &newy);
5691 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5695 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5696 Moving2Blocked(x, y, &newx, &newy);
5698 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5701 MovDir[x][y] = old_move_dir;
5704 else if (move_pattern == MV_WHEN_PUSHED ||
5705 move_pattern == MV_WHEN_DROPPED)
5707 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5708 MovDir[x][y] = MV_NONE;
5712 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5714 static int test_xy[7][2] =
5724 static int test_dir[7] =
5734 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5735 int move_preference = -1000000; /* start with very low preference */
5736 int new_move_dir = MV_NONE;
5737 int start_test = RND(4);
5740 for (i = 0; i < NUM_DIRECTIONS; i++)
5742 int move_dir = test_dir[start_test + i];
5743 int move_dir_preference;
5745 xx = x + test_xy[start_test + i][0];
5746 yy = y + test_xy[start_test + i][1];
5748 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5749 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5751 new_move_dir = move_dir;
5756 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5759 move_dir_preference = -1 * RunnerVisit[xx][yy];
5760 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5761 move_dir_preference = PlayerVisit[xx][yy];
5763 if (move_dir_preference > move_preference)
5765 /* prefer field that has not been visited for the longest time */
5766 move_preference = move_dir_preference;
5767 new_move_dir = move_dir;
5769 else if (move_dir_preference == move_preference &&
5770 move_dir == old_move_dir)
5772 /* prefer last direction when all directions are preferred equally */
5773 move_preference = move_dir_preference;
5774 new_move_dir = move_dir;
5778 MovDir[x][y] = new_move_dir;
5779 if (old_move_dir != new_move_dir)
5780 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5784 static void TurnRound(int x, int y)
5786 int direction = MovDir[x][y];
5788 int element, graphic;
5793 GfxDir[x][y] = MovDir[x][y];
5795 if (direction != MovDir[x][y])
5799 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5802 ResetGfxFrame(x, y, FALSE);
5804 element = Feld[x][y];
5805 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5807 if (graphic_info[graphic].anim_global_sync)
5808 GfxFrame[x][y] = FrameCounter;
5809 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5810 GfxFrame[x][y] = CustomValue[x][y];
5811 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5812 GfxFrame[x][y] = element_info[element].collect_score;
5813 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5814 GfxFrame[x][y] = ChangeDelay[x][y];
5818 static boolean JustBeingPushed(int x, int y)
5822 for (i = 0; i < MAX_PLAYERS; i++)
5824 struct PlayerInfo *player = &stored_player[i];
5826 if (player->active && player->is_pushing && player->MovPos)
5828 int next_jx = player->jx + (player->jx - player->last_jx);
5829 int next_jy = player->jy + (player->jy - player->last_jy);
5831 if (x == next_jx && y == next_jy)
5839 void StartMoving(int x, int y)
5841 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5842 int element = Feld[x][y];
5847 if (MovDelay[x][y] == 0)
5848 GfxAction[x][y] = ACTION_DEFAULT;
5850 if (CAN_FALL(element) && y < lev_fieldy - 1)
5852 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5853 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5854 if (JustBeingPushed(x, y))
5857 if (element == EL_QUICKSAND_FULL)
5859 if (IS_FREE(x, y + 1))
5861 InitMovingField(x, y, MV_DOWN);
5862 started_moving = TRUE;
5864 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5865 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5866 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5867 Store[x][y] = EL_ROCK;
5869 Store[x][y] = EL_ROCK;
5872 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5874 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5876 if (!MovDelay[x][y])
5877 MovDelay[x][y] = TILEY + 1;
5886 Feld[x][y] = EL_QUICKSAND_EMPTY;
5887 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5888 Store[x][y + 1] = Store[x][y];
5891 PlayLevelSoundAction(x, y, ACTION_FILLING);
5894 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5895 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5897 InitMovingField(x, y, MV_DOWN);
5898 started_moving = TRUE;
5900 Feld[x][y] = EL_QUICKSAND_FILLING;
5901 Store[x][y] = element;
5903 PlayLevelSoundAction(x, y, ACTION_FILLING);
5905 else if (element == EL_MAGIC_WALL_FULL)
5907 if (IS_FREE(x, y + 1))
5909 InitMovingField(x, y, MV_DOWN);
5910 started_moving = TRUE;
5912 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5913 Store[x][y] = EL_CHANGED(Store[x][y]);
5915 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5917 if (!MovDelay[x][y])
5918 MovDelay[x][y] = TILEY/4 + 1;
5927 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5928 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5929 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5933 else if (element == EL_BD_MAGIC_WALL_FULL)
5935 if (IS_FREE(x, y + 1))
5937 InitMovingField(x, y, MV_DOWN);
5938 started_moving = TRUE;
5940 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5941 Store[x][y] = EL_CHANGED2(Store[x][y]);
5943 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5945 if (!MovDelay[x][y])
5946 MovDelay[x][y] = TILEY/4 + 1;
5955 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5956 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5957 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5961 else if (CAN_PASS_MAGIC_WALL(element) &&
5962 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5963 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5965 InitMovingField(x, y, MV_DOWN);
5966 started_moving = TRUE;
5969 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5970 EL_BD_MAGIC_WALL_FILLING);
5971 Store[x][y] = element;
5973 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5975 SplashAcid(x, y + 1);
5977 InitMovingField(x, y, MV_DOWN);
5978 started_moving = TRUE;
5980 Store[x][y] = EL_ACID;
5982 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5983 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5985 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5986 CAN_FALL(element) && WasJustFalling[x][y] &&
5987 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5989 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5990 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5991 (Feld[x][y + 1] == EL_BLOCKED)))
5993 /* this is needed for a special case not covered by calling "Impact()"
5994 from "ContinueMoving()": if an element moves to a tile directly below
5995 another element which was just falling on that tile (which was empty
5996 in the previous frame), the falling element above would just stop
5997 instead of smashing the element below (in previous version, the above
5998 element was just checked for "moving" instead of "falling", resulting
5999 in incorrect smashes caused by horizontal movement of the above
6000 element; also, the case of the player being the element to smash was
6001 simply not covered here... :-/ ) */
6003 CheckCollision[x][y] = 0;
6007 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6009 if (MovDir[x][y] == MV_NONE)
6011 InitMovingField(x, y, MV_DOWN);
6012 started_moving = TRUE;
6015 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6017 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6018 MovDir[x][y] = MV_DOWN;
6020 InitMovingField(x, y, MV_DOWN);
6021 started_moving = TRUE;
6023 else if (element == EL_AMOEBA_DROP)
6025 Feld[x][y] = EL_AMOEBA_GROWING;
6026 Store[x][y] = EL_AMOEBA_WET;
6028 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6029 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6030 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6031 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6033 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6034 (IS_FREE(x - 1, y + 1) ||
6035 Feld[x - 1][y + 1] == EL_ACID));
6036 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6037 (IS_FREE(x + 1, y + 1) ||
6038 Feld[x + 1][y + 1] == EL_ACID));
6039 boolean can_fall_any = (can_fall_left || can_fall_right);
6040 boolean can_fall_both = (can_fall_left && can_fall_right);
6041 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6043 #if USE_NEW_ALL_SLIPPERY
6044 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6046 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6047 can_fall_right = FALSE;
6048 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6049 can_fall_left = FALSE;
6050 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6051 can_fall_right = FALSE;
6052 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6053 can_fall_left = FALSE;
6055 can_fall_any = (can_fall_left || can_fall_right);
6056 can_fall_both = FALSE;
6059 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6061 if (slippery_type == SLIPPERY_ONLY_LEFT)
6062 can_fall_right = FALSE;
6063 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6064 can_fall_left = FALSE;
6065 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6066 can_fall_right = FALSE;
6067 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6068 can_fall_left = FALSE;
6070 can_fall_any = (can_fall_left || can_fall_right);
6071 can_fall_both = (can_fall_left && can_fall_right);
6075 #if USE_NEW_ALL_SLIPPERY
6077 #if USE_NEW_SP_SLIPPERY
6078 /* !!! better use the same properties as for custom elements here !!! */
6079 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6080 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6082 can_fall_right = FALSE; /* slip down on left side */
6083 can_fall_both = FALSE;
6088 #if USE_NEW_ALL_SLIPPERY
6091 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6092 can_fall_right = FALSE; /* slip down on left side */
6094 can_fall_left = !(can_fall_right = RND(2));
6096 can_fall_both = FALSE;
6101 if (game.emulation == EMU_BOULDERDASH ||
6102 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6103 can_fall_right = FALSE; /* slip down on left side */
6105 can_fall_left = !(can_fall_right = RND(2));
6107 can_fall_both = FALSE;
6113 /* if not determined otherwise, prefer left side for slipping down */
6114 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6115 started_moving = TRUE;
6119 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6121 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6124 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6125 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6126 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6127 int belt_dir = game.belt_dir[belt_nr];
6129 if ((belt_dir == MV_LEFT && left_is_free) ||
6130 (belt_dir == MV_RIGHT && right_is_free))
6132 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6134 InitMovingField(x, y, belt_dir);
6135 started_moving = TRUE;
6137 Pushed[x][y] = TRUE;
6138 Pushed[nextx][y] = TRUE;
6140 GfxAction[x][y] = ACTION_DEFAULT;
6144 MovDir[x][y] = 0; /* if element was moving, stop it */
6149 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6151 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6153 if (CAN_MOVE(element) && !started_moving)
6156 int move_pattern = element_info[element].move_pattern;
6161 if (MovDir[x][y] == MV_NONE)
6163 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6164 x, y, element, element_info[element].token_name);
6165 printf("StartMoving(): This should never happen!\n");
6170 Moving2Blocked(x, y, &newx, &newy);
6172 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6175 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6176 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6178 WasJustMoving[x][y] = 0;
6179 CheckCollision[x][y] = 0;
6181 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6183 if (Feld[x][y] != element) /* element has changed */
6187 if (!MovDelay[x][y]) /* start new movement phase */
6189 /* all objects that can change their move direction after each step
6190 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6192 if (element != EL_YAMYAM &&
6193 element != EL_DARK_YAMYAM &&
6194 element != EL_PACMAN &&
6195 !(move_pattern & MV_ANY_DIRECTION) &&
6196 move_pattern != MV_TURNING_LEFT &&
6197 move_pattern != MV_TURNING_RIGHT &&
6198 move_pattern != MV_TURNING_LEFT_RIGHT &&
6199 move_pattern != MV_TURNING_RIGHT_LEFT &&
6200 move_pattern != MV_TURNING_RANDOM)
6204 if (MovDelay[x][y] && (element == EL_BUG ||
6205 element == EL_SPACESHIP ||
6206 element == EL_SP_SNIKSNAK ||
6207 element == EL_SP_ELECTRON ||
6208 element == EL_MOLE))
6209 DrawLevelField(x, y);
6213 if (MovDelay[x][y]) /* wait some time before next movement */
6217 if (element == EL_ROBOT ||
6218 element == EL_YAMYAM ||
6219 element == EL_DARK_YAMYAM)
6221 DrawLevelElementAnimationIfNeeded(x, y, element);
6222 PlayLevelSoundAction(x, y, ACTION_WAITING);
6224 else if (element == EL_SP_ELECTRON)
6225 DrawLevelElementAnimationIfNeeded(x, y, element);
6226 else if (element == EL_DRAGON)
6229 int dir = MovDir[x][y];
6230 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6231 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6232 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6233 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6234 dir == MV_UP ? IMG_FLAMES_1_UP :
6235 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6236 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6238 GfxAction[x][y] = ACTION_ATTACKING;
6240 if (IS_PLAYER(x, y))
6241 DrawPlayerField(x, y);
6243 DrawLevelField(x, y);
6245 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6247 for (i = 1; i <= 3; i++)
6249 int xx = x + i * dx;
6250 int yy = y + i * dy;
6251 int sx = SCREENX(xx);
6252 int sy = SCREENY(yy);
6253 int flame_graphic = graphic + (i - 1);
6255 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6260 int flamed = MovingOrBlocked2Element(xx, yy);
6264 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6266 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6267 RemoveMovingField(xx, yy);
6269 RemoveField(xx, yy);
6271 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6274 RemoveMovingField(xx, yy);
6277 ChangeDelay[xx][yy] = 0;
6279 Feld[xx][yy] = EL_FLAMES;
6281 if (IN_SCR_FIELD(sx, sy))
6283 DrawLevelFieldCrumbledSand(xx, yy);
6284 DrawGraphic(sx, sy, flame_graphic, frame);
6289 if (Feld[xx][yy] == EL_FLAMES)
6290 Feld[xx][yy] = EL_EMPTY;
6291 DrawLevelField(xx, yy);
6296 if (MovDelay[x][y]) /* element still has to wait some time */
6298 PlayLevelSoundAction(x, y, ACTION_WAITING);
6304 /* now make next step */
6306 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6308 if (DONT_COLLIDE_WITH(element) &&
6309 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6310 !PLAYER_ENEMY_PROTECTED(newx, newy))
6312 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6317 else if (CAN_MOVE_INTO_ACID(element) &&
6318 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6319 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6320 (MovDir[x][y] == MV_DOWN ||
6321 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6323 SplashAcid(newx, newy);
6324 Store[x][y] = EL_ACID;
6326 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6328 if (Feld[newx][newy] == EL_EXIT_OPEN)
6331 DrawLevelField(x, y);
6333 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6334 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6335 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6337 local_player->friends_still_needed--;
6338 if (!local_player->friends_still_needed &&
6339 !local_player->GameOver && AllPlayersGone)
6340 local_player->LevelSolved = local_player->GameOver = TRUE;
6344 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6346 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6347 DrawLevelField(newx, newy);
6349 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6351 else if (!IS_FREE(newx, newy))
6353 GfxAction[x][y] = ACTION_WAITING;
6355 if (IS_PLAYER(x, y))
6356 DrawPlayerField(x, y);
6358 DrawLevelField(x, y);
6363 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6365 if (IS_FOOD_PIG(Feld[newx][newy]))
6367 if (IS_MOVING(newx, newy))
6368 RemoveMovingField(newx, newy);
6371 Feld[newx][newy] = EL_EMPTY;
6372 DrawLevelField(newx, newy);
6375 PlayLevelSound(x, y, SND_PIG_DIGGING);
6377 else if (!IS_FREE(newx, newy))
6379 if (IS_PLAYER(x, y))
6380 DrawPlayerField(x, y);
6382 DrawLevelField(x, y);
6387 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6389 if (Store[x][y] != EL_EMPTY)
6391 boolean can_clone = FALSE;
6394 /* check if element to clone is still there */
6395 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6397 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6405 /* cannot clone or target field not free anymore -- do not clone */
6406 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6407 Store[x][y] = EL_EMPTY;
6410 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6412 if (IS_MV_DIAGONAL(MovDir[x][y]))
6414 int diagonal_move_dir = MovDir[x][y];
6415 int stored = Store[x][y];
6416 int change_delay = 8;
6419 /* android is moving diagonally */
6421 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6423 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6424 GfxElement[x][y] = EL_EMC_ANDROID;
6425 GfxAction[x][y] = ACTION_SHRINKING;
6426 GfxDir[x][y] = diagonal_move_dir;
6427 ChangeDelay[x][y] = change_delay;
6429 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6432 DrawLevelGraphicAnimation(x, y, graphic);
6433 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6435 if (Feld[newx][newy] == EL_ACID)
6437 SplashAcid(newx, newy);
6442 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6444 Store[newx][newy] = EL_EMC_ANDROID;
6445 GfxElement[newx][newy] = EL_EMC_ANDROID;
6446 GfxAction[newx][newy] = ACTION_GROWING;
6447 GfxDir[newx][newy] = diagonal_move_dir;
6448 ChangeDelay[newx][newy] = change_delay;
6450 graphic = el_act_dir2img(GfxElement[newx][newy],
6451 GfxAction[newx][newy], GfxDir[newx][newy]);
6453 DrawLevelGraphicAnimation(newx, newy, graphic);
6454 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6460 Feld[newx][newy] = EL_EMPTY;
6461 DrawLevelField(newx, newy);
6463 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6466 else if (!IS_FREE(newx, newy))
6469 if (IS_PLAYER(x, y))
6470 DrawPlayerField(x, y);
6472 DrawLevelField(x, y);
6478 else if (IS_CUSTOM_ELEMENT(element) &&
6479 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6481 int new_element = Feld[newx][newy];
6483 if (!IS_FREE(newx, newy))
6485 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6486 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6489 /* no element can dig solid indestructible elements */
6490 if (IS_INDESTRUCTIBLE(new_element) &&
6491 !IS_DIGGABLE(new_element) &&
6492 !IS_COLLECTIBLE(new_element))
6495 if (AmoebaNr[newx][newy] &&
6496 (new_element == EL_AMOEBA_FULL ||
6497 new_element == EL_BD_AMOEBA ||
6498 new_element == EL_AMOEBA_GROWING))
6500 AmoebaCnt[AmoebaNr[newx][newy]]--;
6501 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6504 if (IS_MOVING(newx, newy))
6505 RemoveMovingField(newx, newy);
6508 RemoveField(newx, newy);
6509 DrawLevelField(newx, newy);
6512 /* if digged element was about to explode, prevent the explosion */
6513 ExplodeField[newx][newy] = EX_TYPE_NONE;
6515 PlayLevelSoundAction(x, y, action);
6518 Store[newx][newy] = EL_EMPTY;
6520 /* this makes it possible to leave the removed element again */
6521 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6522 Store[newx][newy] = new_element;
6524 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6526 int move_leave_element = element_info[element].move_leave_element;
6528 /* this makes it possible to leave the removed element again */
6529 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6530 new_element : move_leave_element);
6534 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6536 RunnerVisit[x][y] = FrameCounter;
6537 PlayerVisit[x][y] /= 8; /* expire player visit path */
6540 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6542 if (!IS_FREE(newx, newy))
6544 if (IS_PLAYER(x, y))
6545 DrawPlayerField(x, y);
6547 DrawLevelField(x, y);
6553 boolean wanna_flame = !RND(10);
6554 int dx = newx - x, dy = newy - y;
6555 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6556 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6557 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6558 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6559 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6560 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6563 IS_CLASSIC_ENEMY(element1) ||
6564 IS_CLASSIC_ENEMY(element2)) &&
6565 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6566 element1 != EL_FLAMES && element2 != EL_FLAMES)
6568 ResetGfxAnimation(x, y);
6569 GfxAction[x][y] = ACTION_ATTACKING;
6571 if (IS_PLAYER(x, y))
6572 DrawPlayerField(x, y);
6574 DrawLevelField(x, y);
6576 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6578 MovDelay[x][y] = 50;
6582 RemoveField(newx, newy);
6584 Feld[newx][newy] = EL_FLAMES;
6585 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6588 RemoveField(newx1, newy1);
6590 Feld[newx1][newy1] = EL_FLAMES;
6592 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6595 RemoveField(newx2, newy2);
6597 Feld[newx2][newy2] = EL_FLAMES;
6604 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6605 Feld[newx][newy] == EL_DIAMOND)
6607 if (IS_MOVING(newx, newy))
6608 RemoveMovingField(newx, newy);
6611 Feld[newx][newy] = EL_EMPTY;
6612 DrawLevelField(newx, newy);
6615 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6617 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6618 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6620 if (AmoebaNr[newx][newy])
6622 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6623 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6624 Feld[newx][newy] == EL_BD_AMOEBA)
6625 AmoebaCnt[AmoebaNr[newx][newy]]--;
6630 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6632 RemoveMovingField(newx, newy);
6635 if (IS_MOVING(newx, newy))
6637 RemoveMovingField(newx, newy);
6642 Feld[newx][newy] = EL_EMPTY;
6643 DrawLevelField(newx, newy);
6646 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6648 else if ((element == EL_PACMAN || element == EL_MOLE)
6649 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6651 if (AmoebaNr[newx][newy])
6653 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6654 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6655 Feld[newx][newy] == EL_BD_AMOEBA)
6656 AmoebaCnt[AmoebaNr[newx][newy]]--;
6659 if (element == EL_MOLE)
6661 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6662 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6664 ResetGfxAnimation(x, y);
6665 GfxAction[x][y] = ACTION_DIGGING;
6666 DrawLevelField(x, y);
6668 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6670 return; /* wait for shrinking amoeba */
6672 else /* element == EL_PACMAN */
6674 Feld[newx][newy] = EL_EMPTY;
6675 DrawLevelField(newx, newy);
6676 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6679 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6680 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6681 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6683 /* wait for shrinking amoeba to completely disappear */
6686 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6688 /* object was running against a wall */
6693 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6694 if (move_pattern & MV_ANY_DIRECTION &&
6695 move_pattern == MovDir[x][y])
6697 int blocking_element =
6698 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6700 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6703 element = Feld[x][y]; /* element might have changed */
6707 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6708 DrawLevelElementAnimation(x, y, element);
6710 if (DONT_TOUCH(element))
6711 TestIfBadThingTouchesPlayer(x, y);
6716 InitMovingField(x, y, MovDir[x][y]);
6718 PlayLevelSoundAction(x, y, ACTION_MOVING);
6722 ContinueMoving(x, y);
6725 void ContinueMoving(int x, int y)
6727 int element = Feld[x][y];
6728 struct ElementInfo *ei = &element_info[element];
6729 int direction = MovDir[x][y];
6730 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6731 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6732 int newx = x + dx, newy = y + dy;
6733 int stored = Store[x][y];
6734 int stored_new = Store[newx][newy];
6735 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6736 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6737 boolean last_line = (newy == lev_fieldy - 1);
6739 MovPos[x][y] += getElementMoveStepsize(x, y);
6741 if (pushed_by_player) /* special case: moving object pushed by player */
6742 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6744 if (ABS(MovPos[x][y]) < TILEX)
6746 DrawLevelField(x, y);
6748 return; /* element is still moving */
6751 /* element reached destination field */
6753 Feld[x][y] = EL_EMPTY;
6754 Feld[newx][newy] = element;
6755 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6757 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6759 element = Feld[newx][newy] = EL_ACID;
6761 else if (element == EL_MOLE)
6763 Feld[x][y] = EL_SAND;
6765 DrawLevelFieldCrumbledSandNeighbours(x, y);
6767 else if (element == EL_QUICKSAND_FILLING)
6769 element = Feld[newx][newy] = get_next_element(element);
6770 Store[newx][newy] = Store[x][y];
6772 else if (element == EL_QUICKSAND_EMPTYING)
6774 Feld[x][y] = get_next_element(element);
6775 element = Feld[newx][newy] = Store[x][y];
6777 else if (element == EL_MAGIC_WALL_FILLING)
6779 element = Feld[newx][newy] = get_next_element(element);
6780 if (!game.magic_wall_active)
6781 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6782 Store[newx][newy] = Store[x][y];
6784 else if (element == EL_MAGIC_WALL_EMPTYING)
6786 Feld[x][y] = get_next_element(element);
6787 if (!game.magic_wall_active)
6788 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6789 element = Feld[newx][newy] = Store[x][y];
6791 #if USE_NEW_CUSTOM_VALUE
6792 InitField(newx, newy, FALSE);
6795 else if (element == EL_BD_MAGIC_WALL_FILLING)
6797 element = Feld[newx][newy] = get_next_element(element);
6798 if (!game.magic_wall_active)
6799 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6800 Store[newx][newy] = Store[x][y];
6802 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6804 Feld[x][y] = get_next_element(element);
6805 if (!game.magic_wall_active)
6806 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6807 element = Feld[newx][newy] = Store[x][y];
6809 #if USE_NEW_CUSTOM_VALUE
6810 InitField(newx, newy, FALSE);
6813 else if (element == EL_AMOEBA_DROPPING)
6815 Feld[x][y] = get_next_element(element);
6816 element = Feld[newx][newy] = Store[x][y];
6818 else if (element == EL_SOKOBAN_OBJECT)
6821 Feld[x][y] = Back[x][y];
6823 if (Back[newx][newy])
6824 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6826 Back[x][y] = Back[newx][newy] = 0;
6829 Store[x][y] = EL_EMPTY;
6834 MovDelay[newx][newy] = 0;
6837 if (CAN_CHANGE_OR_HAS_ACTION(element))
6839 if (CAN_CHANGE(element))
6842 /* copy element change control values to new field */
6843 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6844 ChangePage[newx][newy] = ChangePage[x][y];
6845 ChangeCount[newx][newy] = ChangeCount[x][y];
6846 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6849 #if USE_NEW_CUSTOM_VALUE
6850 CustomValue[newx][newy] = CustomValue[x][y];
6856 #if USE_NEW_CUSTOM_VALUE
6857 CustomValue[newx][newy] = CustomValue[x][y];
6861 ChangeDelay[x][y] = 0;
6862 ChangePage[x][y] = -1;
6863 ChangeCount[x][y] = 0;
6864 ChangeEvent[x][y] = -1;
6866 #if USE_NEW_CUSTOM_VALUE
6867 CustomValue[x][y] = 0;
6870 /* copy animation control values to new field */
6871 GfxFrame[newx][newy] = GfxFrame[x][y];
6872 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6873 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6874 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6876 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6878 /* some elements can leave other elements behind after moving */
6880 if (ei->move_leave_element != EL_EMPTY &&
6881 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6882 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6884 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6885 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6886 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6889 int move_leave_element = ei->move_leave_element;
6893 /* this makes it possible to leave the removed element again */
6894 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6895 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6897 /* this makes it possible to leave the removed element again */
6898 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6899 move_leave_element = stored;
6902 /* this makes it possible to leave the removed element again */
6903 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6904 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6905 move_leave_element = stored;
6908 Feld[x][y] = move_leave_element;
6910 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6911 MovDir[x][y] = direction;
6913 InitField(x, y, FALSE);
6915 if (GFX_CRUMBLED(Feld[x][y]))
6916 DrawLevelFieldCrumbledSandNeighbours(x, y);
6918 if (ELEM_IS_PLAYER(move_leave_element))
6919 RelocatePlayer(x, y, move_leave_element);
6922 /* do this after checking for left-behind element */
6923 ResetGfxAnimation(x, y); /* reset animation values for old field */
6925 if (!CAN_MOVE(element) ||
6926 (CAN_FALL(element) && direction == MV_DOWN &&
6927 (element == EL_SPRING ||
6928 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6929 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6930 GfxDir[x][y] = MovDir[newx][newy] = 0;
6932 DrawLevelField(x, y);
6933 DrawLevelField(newx, newy);
6935 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6937 /* prevent pushed element from moving on in pushed direction */
6938 if (pushed_by_player && CAN_MOVE(element) &&
6939 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6940 !(element_info[element].move_pattern & direction))
6941 TurnRound(newx, newy);
6943 /* prevent elements on conveyor belt from moving on in last direction */
6944 if (pushed_by_conveyor && CAN_FALL(element) &&
6945 direction & MV_HORIZONTAL)
6946 MovDir[newx][newy] = 0;
6948 if (!pushed_by_player)
6950 int nextx = newx + dx, nexty = newy + dy;
6951 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6953 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6955 if (CAN_FALL(element) && direction == MV_DOWN)
6956 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6958 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6959 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6962 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6964 TestIfBadThingTouchesPlayer(newx, newy);
6965 TestIfBadThingTouchesFriend(newx, newy);
6967 if (!IS_CUSTOM_ELEMENT(element))
6968 TestIfBadThingTouchesOtherBadThing(newx, newy);
6970 else if (element == EL_PENGUIN)
6971 TestIfFriendTouchesBadThing(newx, newy);
6973 /* give the player one last chance (one more frame) to move away */
6974 if (CAN_FALL(element) && direction == MV_DOWN &&
6975 (last_line || (!IS_FREE(x, newy + 1) &&
6976 (!IS_PLAYER(x, newy + 1) ||
6977 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6980 if (pushed_by_player && !game.use_change_when_pushing_bug)
6982 int push_side = MV_DIR_OPPOSITE(direction);
6983 struct PlayerInfo *player = PLAYERINFO(x, y);
6985 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6986 player->index_bit, push_side);
6987 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6988 player->index_bit, push_side);
6991 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6992 MovDelay[newx][newy] = 1;
6994 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6996 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6999 if (ChangePage[newx][newy] != -1) /* delayed change */
7001 int page = ChangePage[newx][newy];
7002 struct ElementChangeInfo *change = &ei->change_page[page];
7004 ChangePage[newx][newy] = -1;
7006 if (change->can_change)
7008 if (ChangeElement(newx, newy, element, page))
7010 if (change->post_change_function)
7011 change->post_change_function(newx, newy);
7015 if (change->has_action)
7016 ExecuteCustomElementAction(newx, newy, element, page);
7020 TestIfElementHitsCustomElement(newx, newy, direction);
7021 TestIfPlayerTouchesCustomElement(newx, newy);
7022 TestIfElementTouchesCustomElement(newx, newy);
7025 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7026 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7027 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7028 MV_DIR_OPPOSITE(direction));
7032 int AmoebeNachbarNr(int ax, int ay)
7035 int element = Feld[ax][ay];
7037 static int xy[4][2] =
7045 for (i = 0; i < NUM_DIRECTIONS; i++)
7047 int x = ax + xy[i][0];
7048 int y = ay + xy[i][1];
7050 if (!IN_LEV_FIELD(x, y))
7053 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7054 group_nr = AmoebaNr[x][y];
7060 void AmoebenVereinigen(int ax, int ay)
7062 int i, x, y, xx, yy;
7063 int new_group_nr = AmoebaNr[ax][ay];
7064 static int xy[4][2] =
7072 if (new_group_nr == 0)
7075 for (i = 0; i < NUM_DIRECTIONS; i++)
7080 if (!IN_LEV_FIELD(x, y))
7083 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7084 Feld[x][y] == EL_BD_AMOEBA ||
7085 Feld[x][y] == EL_AMOEBA_DEAD) &&
7086 AmoebaNr[x][y] != new_group_nr)
7088 int old_group_nr = AmoebaNr[x][y];
7090 if (old_group_nr == 0)
7093 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7094 AmoebaCnt[old_group_nr] = 0;
7095 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7096 AmoebaCnt2[old_group_nr] = 0;
7099 SCAN_PLAYFIELD(xx, yy)
7101 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7104 if (AmoebaNr[xx][yy] == old_group_nr)
7105 AmoebaNr[xx][yy] = new_group_nr;
7111 void AmoebeUmwandeln(int ax, int ay)
7115 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7117 int group_nr = AmoebaNr[ax][ay];
7122 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7123 printf("AmoebeUmwandeln(): This should never happen!\n");
7129 SCAN_PLAYFIELD(x, y)
7131 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7134 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7137 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7141 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7142 SND_AMOEBA_TURNING_TO_GEM :
7143 SND_AMOEBA_TURNING_TO_ROCK));
7148 static int xy[4][2] =
7156 for (i = 0; i < NUM_DIRECTIONS; i++)
7161 if (!IN_LEV_FIELD(x, y))
7164 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7166 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7167 SND_AMOEBA_TURNING_TO_GEM :
7168 SND_AMOEBA_TURNING_TO_ROCK));
7175 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7178 int group_nr = AmoebaNr[ax][ay];
7179 boolean done = FALSE;
7184 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7185 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7191 SCAN_PLAYFIELD(x, y)
7193 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7196 if (AmoebaNr[x][y] == group_nr &&
7197 (Feld[x][y] == EL_AMOEBA_DEAD ||
7198 Feld[x][y] == EL_BD_AMOEBA ||
7199 Feld[x][y] == EL_AMOEBA_GROWING))
7202 Feld[x][y] = new_element;
7203 InitField(x, y, FALSE);
7204 DrawLevelField(x, y);
7210 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7211 SND_BD_AMOEBA_TURNING_TO_ROCK :
7212 SND_BD_AMOEBA_TURNING_TO_GEM));
7215 void AmoebeWaechst(int x, int y)
7217 static unsigned long sound_delay = 0;
7218 static unsigned long sound_delay_value = 0;
7220 if (!MovDelay[x][y]) /* start new growing cycle */
7224 if (DelayReached(&sound_delay, sound_delay_value))
7226 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7227 sound_delay_value = 30;
7231 if (MovDelay[x][y]) /* wait some time before growing bigger */
7234 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7236 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7237 6 - MovDelay[x][y]);
7239 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7242 if (!MovDelay[x][y])
7244 Feld[x][y] = Store[x][y];
7246 DrawLevelField(x, y);
7251 void AmoebaDisappearing(int x, int y)
7253 static unsigned long sound_delay = 0;
7254 static unsigned long sound_delay_value = 0;
7256 if (!MovDelay[x][y]) /* start new shrinking cycle */
7260 if (DelayReached(&sound_delay, sound_delay_value))
7261 sound_delay_value = 30;
7264 if (MovDelay[x][y]) /* wait some time before shrinking */
7267 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7269 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7270 6 - MovDelay[x][y]);
7272 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7275 if (!MovDelay[x][y])
7277 Feld[x][y] = EL_EMPTY;
7278 DrawLevelField(x, y);
7280 /* don't let mole enter this field in this cycle;
7281 (give priority to objects falling to this field from above) */
7287 void AmoebeAbleger(int ax, int ay)
7290 int element = Feld[ax][ay];
7291 int graphic = el2img(element);
7292 int newax = ax, neway = ay;
7293 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7294 static int xy[4][2] =
7302 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7304 Feld[ax][ay] = EL_AMOEBA_DEAD;
7305 DrawLevelField(ax, ay);
7309 if (IS_ANIMATED(graphic))
7310 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7312 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7313 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7315 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7318 if (MovDelay[ax][ay])
7322 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7325 int x = ax + xy[start][0];
7326 int y = ay + xy[start][1];
7328 if (!IN_LEV_FIELD(x, y))
7331 if (IS_FREE(x, y) ||
7332 CAN_GROW_INTO(Feld[x][y]) ||
7333 Feld[x][y] == EL_QUICKSAND_EMPTY)
7339 if (newax == ax && neway == ay)
7342 else /* normal or "filled" (BD style) amoeba */
7345 boolean waiting_for_player = FALSE;
7347 for (i = 0; i < NUM_DIRECTIONS; i++)
7349 int j = (start + i) % 4;
7350 int x = ax + xy[j][0];
7351 int y = ay + xy[j][1];
7353 if (!IN_LEV_FIELD(x, y))
7356 if (IS_FREE(x, y) ||
7357 CAN_GROW_INTO(Feld[x][y]) ||
7358 Feld[x][y] == EL_QUICKSAND_EMPTY)
7364 else if (IS_PLAYER(x, y))
7365 waiting_for_player = TRUE;
7368 if (newax == ax && neway == ay) /* amoeba cannot grow */
7370 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7372 Feld[ax][ay] = EL_AMOEBA_DEAD;
7373 DrawLevelField(ax, ay);
7374 AmoebaCnt[AmoebaNr[ax][ay]]--;
7376 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7378 if (element == EL_AMOEBA_FULL)
7379 AmoebeUmwandeln(ax, ay);
7380 else if (element == EL_BD_AMOEBA)
7381 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7386 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7388 /* amoeba gets larger by growing in some direction */
7390 int new_group_nr = AmoebaNr[ax][ay];
7393 if (new_group_nr == 0)
7395 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7396 printf("AmoebeAbleger(): This should never happen!\n");
7401 AmoebaNr[newax][neway] = new_group_nr;
7402 AmoebaCnt[new_group_nr]++;
7403 AmoebaCnt2[new_group_nr]++;
7405 /* if amoeba touches other amoeba(s) after growing, unify them */
7406 AmoebenVereinigen(newax, neway);
7408 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7410 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7416 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7417 (neway == lev_fieldy - 1 && newax != ax))
7419 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7420 Store[newax][neway] = element;
7422 else if (neway == ay || element == EL_EMC_DRIPPER)
7424 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7426 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7430 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7431 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7432 Store[ax][ay] = EL_AMOEBA_DROP;
7433 ContinueMoving(ax, ay);
7437 DrawLevelField(newax, neway);
7440 void Life(int ax, int ay)
7444 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7447 int element = Feld[ax][ay];
7448 int graphic = el2img(element);
7449 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7451 boolean changed = FALSE;
7453 if (IS_ANIMATED(graphic))
7454 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7459 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7460 MovDelay[ax][ay] = life_time;
7462 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7465 if (MovDelay[ax][ay])
7469 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7471 int xx = ax+x1, yy = ay+y1;
7474 if (!IN_LEV_FIELD(xx, yy))
7477 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7479 int x = xx+x2, y = yy+y2;
7481 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7484 if (((Feld[x][y] == element ||
7485 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7487 (IS_FREE(x, y) && Stop[x][y]))
7491 if (xx == ax && yy == ay) /* field in the middle */
7493 if (nachbarn < life_parameter[0] ||
7494 nachbarn > life_parameter[1])
7496 Feld[xx][yy] = EL_EMPTY;
7498 DrawLevelField(xx, yy);
7499 Stop[xx][yy] = TRUE;
7503 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7504 { /* free border field */
7505 if (nachbarn >= life_parameter[2] &&
7506 nachbarn <= life_parameter[3])
7508 Feld[xx][yy] = element;
7509 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7511 DrawLevelField(xx, yy);
7512 Stop[xx][yy] = TRUE;
7519 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7520 SND_GAME_OF_LIFE_GROWING);
7523 static void InitRobotWheel(int x, int y)
7525 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7528 static void RunRobotWheel(int x, int y)
7530 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7533 static void StopRobotWheel(int x, int y)
7535 if (ZX == x && ZY == y)
7539 static void InitTimegateWheel(int x, int y)
7541 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7544 static void RunTimegateWheel(int x, int y)
7546 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7549 static void InitMagicBallDelay(int x, int y)
7552 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7554 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7558 static void ActivateMagicBall(int bx, int by)
7562 if (level.ball_random)
7564 int pos_border = RND(8); /* select one of the eight border elements */
7565 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7566 int xx = pos_content % 3;
7567 int yy = pos_content / 3;
7572 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7573 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7577 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7579 int xx = x - bx + 1;
7580 int yy = y - by + 1;
7582 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7583 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7587 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7590 static void InitDiagonalMovingElement(int x, int y)
7593 MovDelay[x][y] = level.android_move_time;
7597 void CheckExit(int x, int y)
7599 if (local_player->gems_still_needed > 0 ||
7600 local_player->sokobanfields_still_needed > 0 ||
7601 local_player->lights_still_needed > 0)
7603 int element = Feld[x][y];
7604 int graphic = el2img(element);
7606 if (IS_ANIMATED(graphic))
7607 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7612 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7615 Feld[x][y] = EL_EXIT_OPENING;
7617 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7620 void CheckExitSP(int x, int y)
7622 if (local_player->gems_still_needed > 0)
7624 int element = Feld[x][y];
7625 int graphic = el2img(element);
7627 if (IS_ANIMATED(graphic))
7628 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7633 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7636 Feld[x][y] = EL_SP_EXIT_OPENING;
7638 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7641 static void CloseAllOpenTimegates()
7646 SCAN_PLAYFIELD(x, y)
7648 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7651 int element = Feld[x][y];
7653 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7655 Feld[x][y] = EL_TIMEGATE_CLOSING;
7657 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7662 void EdelsteinFunkeln(int x, int y)
7664 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7667 if (Feld[x][y] == EL_BD_DIAMOND)
7670 if (MovDelay[x][y] == 0) /* next animation frame */
7671 MovDelay[x][y] = 11 * !SimpleRND(500);
7673 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7677 if (setup.direct_draw && MovDelay[x][y])
7678 SetDrawtoField(DRAW_BUFFERED);
7680 DrawLevelElementAnimation(x, y, Feld[x][y]);
7682 if (MovDelay[x][y] != 0)
7684 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7685 10 - MovDelay[x][y]);
7687 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7689 if (setup.direct_draw)
7693 dest_x = FX + SCREENX(x) * TILEX;
7694 dest_y = FY + SCREENY(y) * TILEY;
7696 BlitBitmap(drawto_field, window,
7697 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7698 SetDrawtoField(DRAW_DIRECT);
7704 void MauerWaechst(int x, int y)
7708 if (!MovDelay[x][y]) /* next animation frame */
7709 MovDelay[x][y] = 3 * delay;
7711 if (MovDelay[x][y]) /* wait some time before next frame */
7715 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7717 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7718 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7720 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7723 if (!MovDelay[x][y])
7725 if (MovDir[x][y] == MV_LEFT)
7727 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7728 DrawLevelField(x - 1, y);
7730 else if (MovDir[x][y] == MV_RIGHT)
7732 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7733 DrawLevelField(x + 1, y);
7735 else if (MovDir[x][y] == MV_UP)
7737 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7738 DrawLevelField(x, y - 1);
7742 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7743 DrawLevelField(x, y + 1);
7746 Feld[x][y] = Store[x][y];
7748 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7749 DrawLevelField(x, y);
7754 void MauerAbleger(int ax, int ay)
7756 int element = Feld[ax][ay];
7757 int graphic = el2img(element);
7758 boolean oben_frei = FALSE, unten_frei = FALSE;
7759 boolean links_frei = FALSE, rechts_frei = FALSE;
7760 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7761 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7762 boolean new_wall = FALSE;
7764 if (IS_ANIMATED(graphic))
7765 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7767 if (!MovDelay[ax][ay]) /* start building new wall */
7768 MovDelay[ax][ay] = 6;
7770 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7773 if (MovDelay[ax][ay])
7777 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7779 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7781 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7783 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7786 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7787 element == EL_EXPANDABLE_WALL_ANY)
7791 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7792 Store[ax][ay-1] = element;
7793 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7794 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7795 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7796 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7801 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7802 Store[ax][ay+1] = element;
7803 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7804 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7805 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7806 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7811 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7812 element == EL_EXPANDABLE_WALL_ANY ||
7813 element == EL_EXPANDABLE_WALL)
7817 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7818 Store[ax-1][ay] = element;
7819 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7820 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7821 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7822 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7828 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7829 Store[ax+1][ay] = element;
7830 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7831 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7832 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7833 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7838 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7839 DrawLevelField(ax, ay);
7841 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7843 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7844 unten_massiv = TRUE;
7845 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7846 links_massiv = TRUE;
7847 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7848 rechts_massiv = TRUE;
7850 if (((oben_massiv && unten_massiv) ||
7851 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7852 element == EL_EXPANDABLE_WALL) &&
7853 ((links_massiv && rechts_massiv) ||
7854 element == EL_EXPANDABLE_WALL_VERTICAL))
7855 Feld[ax][ay] = EL_WALL;
7858 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7861 void CheckForDragon(int x, int y)
7864 boolean dragon_found = FALSE;
7865 static int xy[4][2] =
7873 for (i = 0; i < NUM_DIRECTIONS; i++)
7875 for (j = 0; j < 4; j++)
7877 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7879 if (IN_LEV_FIELD(xx, yy) &&
7880 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7882 if (Feld[xx][yy] == EL_DRAGON)
7883 dragon_found = TRUE;
7892 for (i = 0; i < NUM_DIRECTIONS; i++)
7894 for (j = 0; j < 3; j++)
7896 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7898 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7900 Feld[xx][yy] = EL_EMPTY;
7901 DrawLevelField(xx, yy);
7910 static void InitBuggyBase(int x, int y)
7912 int element = Feld[x][y];
7913 int activating_delay = FRAMES_PER_SECOND / 4;
7916 (element == EL_SP_BUGGY_BASE ?
7917 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7918 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7920 element == EL_SP_BUGGY_BASE_ACTIVE ?
7921 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7924 static void WarnBuggyBase(int x, int y)
7927 static int xy[4][2] =
7935 for (i = 0; i < NUM_DIRECTIONS; i++)
7937 int xx = x + xy[i][0];
7938 int yy = y + xy[i][1];
7940 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7942 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7949 static void InitTrap(int x, int y)
7951 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7954 static void ActivateTrap(int x, int y)
7956 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7959 static void ChangeActiveTrap(int x, int y)
7961 int graphic = IMG_TRAP_ACTIVE;
7963 /* if new animation frame was drawn, correct crumbled sand border */
7964 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7965 DrawLevelFieldCrumbledSand(x, y);
7968 static int getSpecialActionElement(int element, int number, int base_element)
7970 return (element != EL_EMPTY ? element :
7971 number != -1 ? base_element + number - 1 :
7975 static int getModifiedActionNumber(int value_old, int operator, int operand,
7976 int value_min, int value_max)
7978 int value_new = (operator == CA_MODE_SET ? operand :
7979 operator == CA_MODE_ADD ? value_old + operand :
7980 operator == CA_MODE_SUBTRACT ? value_old - operand :
7981 operator == CA_MODE_MULTIPLY ? value_old * operand :
7982 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7983 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7986 return (value_new < value_min ? value_min :
7987 value_new > value_max ? value_max :
7991 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7993 struct ElementInfo *ei = &element_info[element];
7994 struct ElementChangeInfo *change = &ei->change_page[page];
7995 int target_element = change->target_element;
7996 int action_type = change->action_type;
7997 int action_mode = change->action_mode;
7998 int action_arg = change->action_arg;
8001 if (!change->has_action)
8004 /* ---------- determine action paramater values -------------------------- */
8006 int level_time_value =
8007 (level.time > 0 ? TimeLeft :
8010 int action_arg_element =
8011 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8012 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8013 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8016 int action_arg_direction =
8017 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8018 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8019 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8020 change->actual_trigger_side :
8021 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8022 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8025 int action_arg_number_min =
8026 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8029 int action_arg_number_max =
8030 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8031 action_type == CA_SET_LEVEL_GEMS ? 999 :
8032 action_type == CA_SET_LEVEL_TIME ? 9999 :
8033 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8034 action_type == CA_SET_CE_VALUE ? 9999 :
8035 action_type == CA_SET_CE_SCORE ? 9999 :
8038 int action_arg_number_reset =
8039 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8040 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8041 action_type == CA_SET_LEVEL_TIME ? level.time :
8042 action_type == CA_SET_LEVEL_SCORE ? 0 :
8044 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8046 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8048 action_type == CA_SET_CE_SCORE ? 0 :
8051 int action_arg_number =
8052 (action_arg <= CA_ARG_MAX ? action_arg :
8053 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8054 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8055 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8056 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8057 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8058 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8059 #if USE_NEW_CUSTOM_VALUE
8060 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8062 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8064 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8065 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8066 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8067 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8068 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8069 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8070 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8071 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8072 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8073 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8074 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8077 int action_arg_number_old =
8078 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8079 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8080 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8081 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8082 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8085 int action_arg_number_new =
8086 getModifiedActionNumber(action_arg_number_old,
8087 action_mode, action_arg_number,
8088 action_arg_number_min, action_arg_number_max);
8090 int trigger_player_bits =
8091 (change->actual_trigger_player >= EL_PLAYER_1 &&
8092 change->actual_trigger_player <= EL_PLAYER_4 ?
8093 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8096 int action_arg_player_bits =
8097 (action_arg >= CA_ARG_PLAYER_1 &&
8098 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8099 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8102 /* ---------- execute action -------------------------------------------- */
8111 /* ---------- level actions ------------------------------------------- */
8113 case CA_RESTART_LEVEL:
8115 game.restart_level = TRUE;
8120 case CA_SHOW_ENVELOPE:
8122 int element = getSpecialActionElement(action_arg_element,
8123 action_arg_number, EL_ENVELOPE_1);
8125 if (IS_ENVELOPE(element))
8126 local_player->show_envelope = element;
8131 case CA_SET_LEVEL_TIME:
8133 if (level.time > 0) /* only modify limited time value */
8135 TimeLeft = action_arg_number_new;
8137 DrawGameValue_Time(TimeLeft);
8139 if (!TimeLeft && setup.time_limit)
8140 for (i = 0; i < MAX_PLAYERS; i++)
8141 KillPlayer(&stored_player[i]);
8147 case CA_SET_LEVEL_SCORE:
8149 local_player->score = action_arg_number_new;
8151 DrawGameValue_Score(local_player->score);
8156 case CA_SET_LEVEL_GEMS:
8158 local_player->gems_still_needed = action_arg_number_new;
8160 DrawGameValue_Emeralds(local_player->gems_still_needed);
8165 #if !USE_PLAYER_GRAVITY
8166 case CA_SET_LEVEL_GRAVITY:
8168 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8169 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8170 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8176 case CA_SET_LEVEL_WIND:
8178 game.wind_direction = action_arg_direction;
8183 /* ---------- player actions ------------------------------------------ */
8185 case CA_MOVE_PLAYER:
8187 /* automatically move to the next field in specified direction */
8188 for (i = 0; i < MAX_PLAYERS; i++)
8189 if (trigger_player_bits & (1 << i))
8190 stored_player[i].programmed_action = action_arg_direction;
8195 case CA_EXIT_PLAYER:
8197 for (i = 0; i < MAX_PLAYERS; i++)
8198 if (action_arg_player_bits & (1 << i))
8199 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8204 case CA_KILL_PLAYER:
8206 for (i = 0; i < MAX_PLAYERS; i++)
8207 if (action_arg_player_bits & (1 << i))
8208 KillPlayer(&stored_player[i]);
8213 case CA_SET_PLAYER_KEYS:
8215 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8216 int element = getSpecialActionElement(action_arg_element,
8217 action_arg_number, EL_KEY_1);
8219 if (IS_KEY(element))
8221 for (i = 0; i < MAX_PLAYERS; i++)
8223 if (trigger_player_bits & (1 << i))
8225 stored_player[i].key[KEY_NR(element)] = key_state;
8228 DrawGameDoorValues();
8230 DrawGameValue_Keys(stored_player[i].key);
8233 redraw_mask |= REDRAW_DOOR_1;
8241 case CA_SET_PLAYER_SPEED:
8243 for (i = 0; i < MAX_PLAYERS; i++)
8245 if (trigger_player_bits & (1 << i))
8247 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8249 if (action_arg == CA_ARG_SPEED_FASTER &&
8250 stored_player[i].cannot_move)
8252 action_arg_number = STEPSIZE_VERY_SLOW;
8254 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8255 action_arg == CA_ARG_SPEED_FASTER)
8257 action_arg_number = 2;
8258 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8261 else if (action_arg == CA_ARG_NUMBER_RESET)
8263 action_arg_number = level.initial_player_stepsize[i];
8267 getModifiedActionNumber(move_stepsize,
8270 action_arg_number_min,
8271 action_arg_number_max);
8274 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8276 /* make sure that value is power of 2 */
8277 move_stepsize = (1 << log_2(move_stepsize));
8279 /* do no immediately change -- the player might just be moving */
8280 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8282 stored_player[i].cannot_move =
8283 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8291 case CA_SET_PLAYER_SHIELD:
8293 for (i = 0; i < MAX_PLAYERS; i++)
8295 if (trigger_player_bits & (1 << i))
8297 if (action_arg == CA_ARG_SHIELD_OFF)
8299 stored_player[i].shield_normal_time_left = 0;
8300 stored_player[i].shield_deadly_time_left = 0;
8302 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8304 stored_player[i].shield_normal_time_left = 999999;
8306 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8308 stored_player[i].shield_normal_time_left = 999999;
8309 stored_player[i].shield_deadly_time_left = 999999;
8317 #if USE_PLAYER_GRAVITY
8318 case CA_SET_PLAYER_GRAVITY:
8320 for (i = 0; i < MAX_PLAYERS; i++)
8322 if (trigger_player_bits & (1 << i))
8324 stored_player[i].gravity =
8325 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8326 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8327 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8328 stored_player[i].gravity);
8336 case CA_SET_PLAYER_ARTWORK:
8338 for (i = 0; i < MAX_PLAYERS; i++)
8340 if (trigger_player_bits & (1 << i))
8342 int artwork_element = action_arg_element;
8344 if (action_arg == CA_ARG_ELEMENT_RESET)
8346 (level.use_artwork_element[i] ? level.artwork_element[i] :
8347 stored_player[i].element_nr);
8349 stored_player[i].artwork_element = artwork_element;
8351 SetPlayerWaiting(&stored_player[i], FALSE);
8353 /* set number of special actions for bored and sleeping animation */
8354 stored_player[i].num_special_action_bored =
8355 get_num_special_action(artwork_element,
8356 ACTION_BORING_1, ACTION_BORING_LAST);
8357 stored_player[i].num_special_action_sleeping =
8358 get_num_special_action(artwork_element,
8359 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8366 /* ---------- CE actions ---------------------------------------------- */
8368 case CA_SET_CE_VALUE:
8370 #if USE_NEW_CUSTOM_VALUE
8371 int last_ce_value = CustomValue[x][y];
8373 CustomValue[x][y] = action_arg_number_new;
8376 printf("::: CE value == %d\n", CustomValue[x][y]);
8379 if (CustomValue[x][y] != last_ce_value)
8381 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8382 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8384 if (CustomValue[x][y] == 0)
8387 printf("::: CE_VALUE_GETS_ZERO\n");
8390 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8391 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8394 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8404 case CA_SET_CE_SCORE:
8406 #if USE_NEW_CUSTOM_VALUE
8407 int last_ce_score = ei->collect_score;
8409 ei->collect_score = action_arg_number_new;
8412 printf("::: CE score == %d\n", ei->collect_score);
8415 if (ei->collect_score != last_ce_score)
8417 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8418 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8420 if (ei->collect_score == 0)
8423 printf("::: CE_SCORE_GETS_ZERO\n");
8426 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8427 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8430 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8440 /* ---------- engine actions ------------------------------------------ */
8442 case CA_SET_ENGINE_SCAN_MODE:
8444 InitPlayfieldScanMode(action_arg);
8454 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8456 int old_element = Feld[x][y];
8457 int new_element = get_element_from_group_element(element);
8458 int previous_move_direction = MovDir[x][y];
8459 #if USE_NEW_CUSTOM_VALUE
8460 int last_ce_value = CustomValue[x][y];
8462 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8463 boolean add_player_onto_element = (new_element_is_player &&
8464 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8465 IS_WALKABLE(old_element));
8468 /* check if element under the player changes from accessible to unaccessible
8469 (needed for special case of dropping element which then changes) */
8470 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8471 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8479 if (!add_player_onto_element)
8481 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8482 RemoveMovingField(x, y);
8486 Feld[x][y] = new_element;
8488 #if !USE_GFX_RESET_GFX_ANIMATION
8489 ResetGfxAnimation(x, y);
8490 ResetRandomAnimationValue(x, y);
8493 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8494 MovDir[x][y] = previous_move_direction;
8496 #if USE_NEW_CUSTOM_VALUE
8497 if (element_info[new_element].use_last_ce_value)
8498 CustomValue[x][y] = last_ce_value;
8501 InitField_WithBug1(x, y, FALSE);
8503 new_element = Feld[x][y]; /* element may have changed */
8505 #if USE_GFX_RESET_GFX_ANIMATION
8506 ResetGfxAnimation(x, y);
8507 ResetRandomAnimationValue(x, y);
8510 DrawLevelField(x, y);
8512 if (GFX_CRUMBLED(new_element))
8513 DrawLevelFieldCrumbledSandNeighbours(x, y);
8517 /* check if element under the player changes from accessible to unaccessible
8518 (needed for special case of dropping element which then changes) */
8519 /* (must be checked after creating new element for walkable group elements) */
8520 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8521 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8529 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8530 if (new_element_is_player)
8531 RelocatePlayer(x, y, new_element);
8534 ChangeCount[x][y]++; /* count number of changes in the same frame */
8536 TestIfBadThingTouchesPlayer(x, y);
8537 TestIfPlayerTouchesCustomElement(x, y);
8538 TestIfElementTouchesCustomElement(x, y);
8541 static void CreateField(int x, int y, int element)
8543 CreateFieldExt(x, y, element, FALSE);
8546 static void CreateElementFromChange(int x, int y, int element)
8548 element = GET_VALID_RUNTIME_ELEMENT(element);
8550 #if USE_STOP_CHANGED_ELEMENTS
8551 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8553 int old_element = Feld[x][y];
8555 /* prevent changed element from moving in same engine frame
8556 unless both old and new element can either fall or move */
8557 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8558 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8563 CreateFieldExt(x, y, element, TRUE);
8566 static boolean ChangeElement(int x, int y, int element, int page)
8568 struct ElementInfo *ei = &element_info[element];
8569 struct ElementChangeInfo *change = &ei->change_page[page];
8570 int ce_value = CustomValue[x][y];
8571 int ce_score = ei->collect_score;
8573 int old_element = Feld[x][y];
8575 /* always use default change event to prevent running into a loop */
8576 if (ChangeEvent[x][y] == -1)
8577 ChangeEvent[x][y] = CE_DELAY;
8579 if (ChangeEvent[x][y] == CE_DELAY)
8581 /* reset actual trigger element, trigger player and action element */
8582 change->actual_trigger_element = EL_EMPTY;
8583 change->actual_trigger_player = EL_PLAYER_1;
8584 change->actual_trigger_side = CH_SIDE_NONE;
8585 change->actual_trigger_ce_value = 0;
8586 change->actual_trigger_ce_score = 0;
8589 /* do not change elements more than a specified maximum number of changes */
8590 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8593 ChangeCount[x][y]++; /* count number of changes in the same frame */
8595 if (change->explode)
8602 if (change->use_target_content)
8604 boolean complete_replace = TRUE;
8605 boolean can_replace[3][3];
8608 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8611 boolean is_walkable;
8612 boolean is_diggable;
8613 boolean is_collectible;
8614 boolean is_removable;
8615 boolean is_destructible;
8616 int ex = x + xx - 1;
8617 int ey = y + yy - 1;
8618 int content_element = change->target_content.e[xx][yy];
8621 can_replace[xx][yy] = TRUE;
8623 if (ex == x && ey == y) /* do not check changing element itself */
8626 if (content_element == EL_EMPTY_SPACE)
8628 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8633 if (!IN_LEV_FIELD(ex, ey))
8635 can_replace[xx][yy] = FALSE;
8636 complete_replace = FALSE;
8643 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8644 e = MovingOrBlocked2Element(ex, ey);
8646 is_empty = (IS_FREE(ex, ey) ||
8647 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8649 is_walkable = (is_empty || IS_WALKABLE(e));
8650 is_diggable = (is_empty || IS_DIGGABLE(e));
8651 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8652 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8653 is_removable = (is_diggable || is_collectible);
8655 can_replace[xx][yy] =
8656 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8657 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8658 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8659 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8660 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8661 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8662 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8664 if (!can_replace[xx][yy])
8665 complete_replace = FALSE;
8668 if (!change->only_if_complete || complete_replace)
8670 boolean something_has_changed = FALSE;
8672 if (change->only_if_complete && change->use_random_replace &&
8673 RND(100) < change->random_percentage)
8676 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8678 int ex = x + xx - 1;
8679 int ey = y + yy - 1;
8680 int content_element;
8682 if (can_replace[xx][yy] && (!change->use_random_replace ||
8683 RND(100) < change->random_percentage))
8685 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8686 RemoveMovingField(ex, ey);
8688 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8690 content_element = change->target_content.e[xx][yy];
8691 target_element = GET_TARGET_ELEMENT(content_element, change,
8692 ce_value, ce_score);
8694 CreateElementFromChange(ex, ey, target_element);
8696 something_has_changed = TRUE;
8698 /* for symmetry reasons, freeze newly created border elements */
8699 if (ex != x || ey != y)
8700 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8704 if (something_has_changed)
8706 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8707 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8713 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8714 ce_value, ce_score);
8716 if (element == EL_DIAGONAL_GROWING ||
8717 element == EL_DIAGONAL_SHRINKING)
8719 target_element = Store[x][y];
8721 Store[x][y] = EL_EMPTY;
8724 CreateElementFromChange(x, y, target_element);
8726 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8727 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8730 /* this uses direct change before indirect change */
8731 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8736 #if USE_NEW_DELAYED_ACTION
8738 static void HandleElementChange(int x, int y, int page)
8740 int element = MovingOrBlocked2Element(x, y);
8741 struct ElementInfo *ei = &element_info[element];
8742 struct ElementChangeInfo *change = &ei->change_page[page];
8745 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8746 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8749 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8750 x, y, element, element_info[element].token_name);
8751 printf("HandleElementChange(): This should never happen!\n");
8756 /* this can happen with classic bombs on walkable, changing elements */
8757 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8760 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8761 ChangeDelay[x][y] = 0;
8767 if (ChangeDelay[x][y] == 0) /* initialize element change */
8769 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8771 if (change->can_change)
8773 ResetGfxAnimation(x, y);
8774 ResetRandomAnimationValue(x, y);
8776 if (change->pre_change_function)
8777 change->pre_change_function(x, y);
8781 ChangeDelay[x][y]--;
8783 if (ChangeDelay[x][y] != 0) /* continue element change */
8785 if (change->can_change)
8787 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8789 if (IS_ANIMATED(graphic))
8790 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8792 if (change->change_function)
8793 change->change_function(x, y);
8796 else /* finish element change */
8798 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8800 page = ChangePage[x][y];
8801 ChangePage[x][y] = -1;
8803 change = &ei->change_page[page];
8806 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8808 ChangeDelay[x][y] = 1; /* try change after next move step */
8809 ChangePage[x][y] = page; /* remember page to use for change */
8814 if (change->can_change)
8816 if (ChangeElement(x, y, element, page))
8818 if (change->post_change_function)
8819 change->post_change_function(x, y);
8823 if (change->has_action)
8824 ExecuteCustomElementAction(x, y, element, page);
8830 static void HandleElementChange(int x, int y, int page)
8832 int element = MovingOrBlocked2Element(x, y);
8833 struct ElementInfo *ei = &element_info[element];
8834 struct ElementChangeInfo *change = &ei->change_page[page];
8837 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8840 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8841 x, y, element, element_info[element].token_name);
8842 printf("HandleElementChange(): This should never happen!\n");
8847 /* this can happen with classic bombs on walkable, changing elements */
8848 if (!CAN_CHANGE(element))
8851 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8852 ChangeDelay[x][y] = 0;
8858 if (ChangeDelay[x][y] == 0) /* initialize element change */
8860 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8862 ResetGfxAnimation(x, y);
8863 ResetRandomAnimationValue(x, y);
8865 if (change->pre_change_function)
8866 change->pre_change_function(x, y);
8869 ChangeDelay[x][y]--;
8871 if (ChangeDelay[x][y] != 0) /* continue element change */
8873 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8875 if (IS_ANIMATED(graphic))
8876 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8878 if (change->change_function)
8879 change->change_function(x, y);
8881 else /* finish element change */
8883 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8885 page = ChangePage[x][y];
8886 ChangePage[x][y] = -1;
8888 change = &ei->change_page[page];
8891 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8893 ChangeDelay[x][y] = 1; /* try change after next move step */
8894 ChangePage[x][y] = page; /* remember page to use for change */
8899 if (ChangeElement(x, y, element, page))
8901 if (change->post_change_function)
8902 change->post_change_function(x, y);
8909 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8910 int trigger_element,
8916 boolean change_done_any = FALSE;
8917 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8920 if (!(trigger_events[trigger_element][trigger_event]))
8923 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8925 int element = EL_CUSTOM_START + i;
8926 boolean change_done = FALSE;
8929 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8930 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8933 for (p = 0; p < element_info[element].num_change_pages; p++)
8935 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8937 if (change->can_change_or_has_action &&
8938 change->has_event[trigger_event] &&
8939 change->trigger_side & trigger_side &&
8940 change->trigger_player & trigger_player &&
8941 change->trigger_page & trigger_page_bits &&
8942 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8944 change->actual_trigger_element = trigger_element;
8945 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8946 change->actual_trigger_side = trigger_side;
8947 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8948 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8950 if ((change->can_change && !change_done) || change->has_action)
8955 SCAN_PLAYFIELD(x, y)
8957 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8960 if (Feld[x][y] == element)
8962 if (change->can_change && !change_done)
8964 ChangeDelay[x][y] = 1;
8965 ChangeEvent[x][y] = trigger_event;
8967 HandleElementChange(x, y, p);
8969 #if USE_NEW_DELAYED_ACTION
8970 else if (change->has_action)
8972 ExecuteCustomElementAction(x, y, element, p);
8973 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8976 if (change->has_action)
8978 ExecuteCustomElementAction(x, y, element, p);
8979 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8985 if (change->can_change)
8988 change_done_any = TRUE;
8995 return change_done_any;
8998 static boolean CheckElementChangeExt(int x, int y,
9000 int trigger_element,
9005 boolean change_done = FALSE;
9008 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9009 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9012 if (Feld[x][y] == EL_BLOCKED)
9014 Blocked2Moving(x, y, &x, &y);
9015 element = Feld[x][y];
9019 /* check if element has already changed */
9020 if (Feld[x][y] != element)
9023 /* check if element has already changed or is about to change after moving */
9024 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9025 Feld[x][y] != element) ||
9027 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9028 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9029 ChangePage[x][y] != -1)))
9033 for (p = 0; p < element_info[element].num_change_pages; p++)
9035 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9037 boolean check_trigger_element =
9038 (trigger_event == CE_TOUCHING_X ||
9039 trigger_event == CE_HITTING_X ||
9040 trigger_event == CE_HIT_BY_X);
9042 if (change->can_change_or_has_action &&
9043 change->has_event[trigger_event] &&
9044 change->trigger_side & trigger_side &&
9045 change->trigger_player & trigger_player &&
9046 (!check_trigger_element ||
9047 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9049 change->actual_trigger_element = trigger_element;
9050 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9051 change->actual_trigger_side = trigger_side;
9052 change->actual_trigger_ce_value = CustomValue[x][y];
9053 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9055 /* special case: trigger element not at (x,y) position for some events */
9056 if (check_trigger_element)
9068 { 0, 0 }, { 0, 0 }, { 0, 0 },
9072 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9073 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9075 change->actual_trigger_ce_value = CustomValue[xx][yy];
9076 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9079 if (change->can_change && !change_done)
9081 ChangeDelay[x][y] = 1;
9082 ChangeEvent[x][y] = trigger_event;
9084 HandleElementChange(x, y, p);
9088 #if USE_NEW_DELAYED_ACTION
9089 else if (change->has_action)
9091 ExecuteCustomElementAction(x, y, element, p);
9092 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9095 if (change->has_action)
9097 ExecuteCustomElementAction(x, y, element, p);
9098 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9107 static void PlayPlayerSound(struct PlayerInfo *player)
9109 int jx = player->jx, jy = player->jy;
9110 int sound_element = player->artwork_element;
9111 int last_action = player->last_action_waiting;
9112 int action = player->action_waiting;
9114 if (player->is_waiting)
9116 if (action != last_action)
9117 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9119 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9123 if (action != last_action)
9124 StopSound(element_info[sound_element].sound[last_action]);
9126 if (last_action == ACTION_SLEEPING)
9127 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9131 static void PlayAllPlayersSound()
9135 for (i = 0; i < MAX_PLAYERS; i++)
9136 if (stored_player[i].active)
9137 PlayPlayerSound(&stored_player[i]);
9140 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9142 boolean last_waiting = player->is_waiting;
9143 int move_dir = player->MovDir;
9145 player->dir_waiting = move_dir;
9146 player->last_action_waiting = player->action_waiting;
9150 if (!last_waiting) /* not waiting -> waiting */
9152 player->is_waiting = TRUE;
9154 player->frame_counter_bored =
9156 game.player_boring_delay_fixed +
9157 SimpleRND(game.player_boring_delay_random);
9158 player->frame_counter_sleeping =
9160 game.player_sleeping_delay_fixed +
9161 SimpleRND(game.player_sleeping_delay_random);
9164 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9166 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9170 if (game.player_sleeping_delay_fixed +
9171 game.player_sleeping_delay_random > 0 &&
9172 player->anim_delay_counter == 0 &&
9173 player->post_delay_counter == 0 &&
9174 FrameCounter >= player->frame_counter_sleeping)
9175 player->is_sleeping = TRUE;
9176 else if (game.player_boring_delay_fixed +
9177 game.player_boring_delay_random > 0 &&
9178 FrameCounter >= player->frame_counter_bored)
9179 player->is_bored = TRUE;
9181 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9182 player->is_bored ? ACTION_BORING :
9186 if (player->is_sleeping && player->use_murphy)
9188 /* special case for sleeping Murphy when leaning against non-free tile */
9190 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9191 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9192 !IS_MOVING(player->jx - 1, player->jy)))
9194 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9195 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9196 !IS_MOVING(player->jx + 1, player->jy)))
9197 move_dir = MV_RIGHT;
9199 player->is_sleeping = FALSE;
9201 player->dir_waiting = move_dir;
9205 if (player->is_sleeping)
9207 if (player->num_special_action_sleeping > 0)
9209 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9211 int last_special_action = player->special_action_sleeping;
9212 int num_special_action = player->num_special_action_sleeping;
9213 int special_action =
9214 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9215 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9216 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9217 last_special_action + 1 : ACTION_SLEEPING);
9218 int special_graphic =
9219 el_act_dir2img(player->artwork_element, special_action, move_dir);
9221 player->anim_delay_counter =
9222 graphic_info[special_graphic].anim_delay_fixed +
9223 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9224 player->post_delay_counter =
9225 graphic_info[special_graphic].post_delay_fixed +
9226 SimpleRND(graphic_info[special_graphic].post_delay_random);
9228 player->special_action_sleeping = special_action;
9231 if (player->anim_delay_counter > 0)
9233 player->action_waiting = player->special_action_sleeping;
9234 player->anim_delay_counter--;
9236 else if (player->post_delay_counter > 0)
9238 player->post_delay_counter--;
9242 else if (player->is_bored)
9244 if (player->num_special_action_bored > 0)
9246 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9248 int special_action =
9249 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9250 int special_graphic =
9251 el_act_dir2img(player->artwork_element, special_action, move_dir);
9253 player->anim_delay_counter =
9254 graphic_info[special_graphic].anim_delay_fixed +
9255 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9256 player->post_delay_counter =
9257 graphic_info[special_graphic].post_delay_fixed +
9258 SimpleRND(graphic_info[special_graphic].post_delay_random);
9260 player->special_action_bored = special_action;
9263 if (player->anim_delay_counter > 0)
9265 player->action_waiting = player->special_action_bored;
9266 player->anim_delay_counter--;
9268 else if (player->post_delay_counter > 0)
9270 player->post_delay_counter--;
9275 else if (last_waiting) /* waiting -> not waiting */
9277 player->is_waiting = FALSE;
9278 player->is_bored = FALSE;
9279 player->is_sleeping = FALSE;
9281 player->frame_counter_bored = -1;
9282 player->frame_counter_sleeping = -1;
9284 player->anim_delay_counter = 0;
9285 player->post_delay_counter = 0;
9287 player->dir_waiting = player->MovDir;
9288 player->action_waiting = ACTION_DEFAULT;
9290 player->special_action_bored = ACTION_DEFAULT;
9291 player->special_action_sleeping = ACTION_DEFAULT;
9295 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9297 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9298 int left = player_action & JOY_LEFT;
9299 int right = player_action & JOY_RIGHT;
9300 int up = player_action & JOY_UP;
9301 int down = player_action & JOY_DOWN;
9302 int button1 = player_action & JOY_BUTTON_1;
9303 int button2 = player_action & JOY_BUTTON_2;
9304 int dx = (left ? -1 : right ? 1 : 0);
9305 int dy = (up ? -1 : down ? 1 : 0);
9307 if (!player->active || tape.pausing)
9313 snapped = SnapField(player, dx, dy);
9317 dropped = DropElement(player);
9319 moved = MovePlayer(player, dx, dy);
9322 if (tape.single_step && tape.recording && !tape.pausing)
9324 if (button1 || (dropped && !moved))
9326 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9327 SnapField(player, 0, 0); /* stop snapping */
9331 SetPlayerWaiting(player, FALSE);
9333 return player_action;
9337 /* no actions for this player (no input at player's configured device) */
9339 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9340 SnapField(player, 0, 0);
9341 CheckGravityMovementWhenNotMoving(player);
9343 if (player->MovPos == 0)
9344 SetPlayerWaiting(player, TRUE);
9346 if (player->MovPos == 0) /* needed for tape.playing */
9347 player->is_moving = FALSE;
9349 player->is_dropping = FALSE;
9350 player->is_dropping_pressed = FALSE;
9351 player->drop_pressed_delay = 0;
9357 static void CheckLevelTime()
9361 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9363 if (level.native_em_level->lev->home == 0) /* all players at home */
9365 local_player->LevelSolved = TRUE;
9366 AllPlayersGone = TRUE;
9368 level.native_em_level->lev->home = -1;
9371 if (level.native_em_level->ply[0]->alive == 0 &&
9372 level.native_em_level->ply[1]->alive == 0 &&
9373 level.native_em_level->ply[2]->alive == 0 &&
9374 level.native_em_level->ply[3]->alive == 0) /* all dead */
9375 AllPlayersGone = TRUE;
9378 if (TimeFrames >= FRAMES_PER_SECOND)
9383 for (i = 0; i < MAX_PLAYERS; i++)
9385 struct PlayerInfo *player = &stored_player[i];
9387 if (SHIELD_ON(player))
9389 player->shield_normal_time_left--;
9391 if (player->shield_deadly_time_left > 0)
9392 player->shield_deadly_time_left--;
9396 if (!level.use_step_counter)
9404 if (TimeLeft <= 10 && setup.time_limit)
9405 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9407 DrawGameValue_Time(TimeLeft);
9409 if (!TimeLeft && setup.time_limit)
9411 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9412 level.native_em_level->lev->killed_out_of_time = TRUE;
9414 for (i = 0; i < MAX_PLAYERS; i++)
9415 KillPlayer(&stored_player[i]);
9418 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9419 DrawGameValue_Time(TimePlayed);
9421 level.native_em_level->lev->time =
9422 (level.time == 0 ? TimePlayed : TimeLeft);
9425 if (tape.recording || tape.playing)
9426 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9430 void AdvanceFrameAndPlayerCounters(int player_nr)
9435 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9436 FrameCounter, FrameCounter + 1);
9439 /* advance frame counters (global frame counter and time frame counter) */
9443 /* advance player counters (counters for move delay, move animation etc.) */
9444 for (i = 0; i < MAX_PLAYERS; i++)
9446 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9447 int move_delay_value = stored_player[i].move_delay_value;
9448 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9450 if (!advance_player_counters) /* not all players may be affected */
9453 #if USE_NEW_PLAYER_ANIM
9454 if (move_frames == 0) /* less than one move per game frame */
9456 int stepsize = TILEX / move_delay_value;
9457 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9458 int count = (stored_player[i].is_moving ?
9459 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9461 if (count % delay == 0)
9466 stored_player[i].Frame += move_frames;
9468 if (stored_player[i].MovPos != 0)
9469 stored_player[i].StepFrame += move_frames;
9471 if (stored_player[i].move_delay > 0)
9472 stored_player[i].move_delay--;
9474 /* due to bugs in previous versions, counter must count up, not down */
9475 if (stored_player[i].push_delay != -1)
9476 stored_player[i].push_delay++;
9478 if (stored_player[i].drop_delay > 0)
9479 stored_player[i].drop_delay--;
9481 if (stored_player[i].is_dropping_pressed)
9482 stored_player[i].drop_pressed_delay++;
9486 void StartGameActions(boolean init_network_game, boolean record_tape,
9489 unsigned long new_random_seed = InitRND(random_seed);
9492 TapeStartRecording(new_random_seed);
9494 #if defined(NETWORK_AVALIABLE)
9495 if (init_network_game)
9497 SendToServer_StartPlaying();
9505 game_status = GAME_MODE_PLAYING;
9512 static unsigned long game_frame_delay = 0;
9513 unsigned long game_frame_delay_value;
9514 byte *recorded_player_action;
9515 byte summarized_player_action = 0;
9516 byte tape_action[MAX_PLAYERS];
9519 if (game.restart_level)
9520 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9522 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9524 if (level.native_em_level->lev->home == 0) /* all players at home */
9526 local_player->LevelSolved = TRUE;
9527 AllPlayersGone = TRUE;
9529 level.native_em_level->lev->home = -1;
9532 if (level.native_em_level->ply[0]->alive == 0 &&
9533 level.native_em_level->ply[1]->alive == 0 &&
9534 level.native_em_level->ply[2]->alive == 0 &&
9535 level.native_em_level->ply[3]->alive == 0) /* all dead */
9536 AllPlayersGone = TRUE;
9539 if (local_player->LevelSolved)
9542 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9545 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9548 game_frame_delay_value =
9549 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9551 if (tape.playing && tape.warp_forward && !tape.pausing)
9552 game_frame_delay_value = 0;
9554 /* ---------- main game synchronization point ---------- */
9556 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9558 if (network_playing && !network_player_action_received)
9560 /* try to get network player actions in time */
9562 #if defined(NETWORK_AVALIABLE)
9563 /* last chance to get network player actions without main loop delay */
9567 /* game was quit by network peer */
9568 if (game_status != GAME_MODE_PLAYING)
9571 if (!network_player_action_received)
9572 return; /* failed to get network player actions in time */
9574 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9580 /* at this point we know that we really continue executing the game */
9583 network_player_action_received = FALSE;
9586 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9588 if (tape.set_centered_player)
9590 game.centered_player_nr_next = tape.centered_player_nr_next;
9591 game.set_centered_player = TRUE;
9594 for (i = 0; i < MAX_PLAYERS; i++)
9596 summarized_player_action |= stored_player[i].action;
9598 if (!network_playing)
9599 stored_player[i].effective_action = stored_player[i].action;
9602 #if defined(NETWORK_AVALIABLE)
9603 if (network_playing)
9604 SendToServer_MovePlayer(summarized_player_action);
9607 if (!options.network && !setup.team_mode)
9608 local_player->effective_action = summarized_player_action;
9610 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9612 for (i = 0; i < MAX_PLAYERS; i++)
9613 stored_player[i].effective_action =
9614 (i == game.centered_player_nr ? summarized_player_action : 0);
9617 if (recorded_player_action != NULL)
9618 for (i = 0; i < MAX_PLAYERS; i++)
9619 stored_player[i].effective_action = recorded_player_action[i];
9621 for (i = 0; i < MAX_PLAYERS; i++)
9623 tape_action[i] = stored_player[i].effective_action;
9625 /* (this can only happen in the R'n'D game engine) */
9626 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9627 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9630 /* only record actions from input devices, but not programmed actions */
9632 TapeRecordAction(tape_action);
9634 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9636 GameActions_EM_Main();
9644 void GameActions_EM_Main()
9646 byte effective_action[MAX_PLAYERS];
9647 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9650 for (i = 0; i < MAX_PLAYERS; i++)
9651 effective_action[i] = stored_player[i].effective_action;
9653 GameActions_EM(effective_action, warp_mode);
9657 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9660 void GameActions_RND()
9662 int magic_wall_x = 0, magic_wall_y = 0;
9663 int i, x, y, element, graphic;
9665 InitPlayfieldScanModeVars();
9667 #if USE_ONE_MORE_CHANGE_PER_FRAME
9668 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9670 SCAN_PLAYFIELD(x, y)
9672 ChangeCount[x][y] = 0;
9673 ChangeEvent[x][y] = -1;
9679 if (game.set_centered_player)
9681 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9683 /* switching to "all players" only possible if all players fit to screen */
9684 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9686 game.centered_player_nr_next = game.centered_player_nr;
9687 game.set_centered_player = FALSE;
9690 /* do not switch focus to non-existing (or non-active) player */
9691 if (game.centered_player_nr_next >= 0 &&
9692 !stored_player[game.centered_player_nr_next].active)
9694 game.centered_player_nr_next = game.centered_player_nr;
9695 game.set_centered_player = FALSE;
9699 if (game.set_centered_player &&
9700 ScreenMovPos == 0) /* screen currently aligned at tile position */
9704 if (game.centered_player_nr_next == -1)
9706 setScreenCenteredToAllPlayers(&sx, &sy);
9710 sx = stored_player[game.centered_player_nr_next].jx;
9711 sy = stored_player[game.centered_player_nr_next].jy;
9714 game.centered_player_nr = game.centered_player_nr_next;
9715 game.set_centered_player = FALSE;
9717 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9718 DrawGameDoorValues();
9722 for (i = 0; i < MAX_PLAYERS; i++)
9724 int actual_player_action = stored_player[i].effective_action;
9727 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9728 - rnd_equinox_tetrachloride 048
9729 - rnd_equinox_tetrachloride_ii 096
9730 - rnd_emanuel_schmieg 002
9731 - doctor_sloan_ww 001, 020
9733 if (stored_player[i].MovPos == 0)
9734 CheckGravityMovement(&stored_player[i]);
9737 /* overwrite programmed action with tape action */
9738 if (stored_player[i].programmed_action)
9739 actual_player_action = stored_player[i].programmed_action;
9742 PlayerActions(&stored_player[i], actual_player_action);
9744 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9746 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9747 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9750 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9754 network_player_action_received = FALSE;
9757 ScrollScreen(NULL, SCROLL_GO_ON);
9759 /* for backwards compatibility, the following code emulates a fixed bug that
9760 occured when pushing elements (causing elements that just made their last
9761 pushing step to already (if possible) make their first falling step in the
9762 same game frame, which is bad); this code is also needed to use the famous
9763 "spring push bug" which is used in older levels and might be wanted to be
9764 used also in newer levels, but in this case the buggy pushing code is only
9765 affecting the "spring" element and no other elements */
9767 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9769 for (i = 0; i < MAX_PLAYERS; i++)
9771 struct PlayerInfo *player = &stored_player[i];
9775 if (player->active && player->is_pushing && player->is_moving &&
9777 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9778 Feld[x][y] == EL_SPRING))
9780 ContinueMoving(x, y);
9782 /* continue moving after pushing (this is actually a bug) */
9783 if (!IS_MOVING(x, y))
9792 SCAN_PLAYFIELD(x, y)
9794 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9797 ChangeCount[x][y] = 0;
9798 ChangeEvent[x][y] = -1;
9800 /* this must be handled before main playfield loop */
9801 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9804 if (MovDelay[x][y] <= 0)
9808 #if USE_NEW_SNAP_DELAY
9809 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9812 if (MovDelay[x][y] <= 0)
9815 DrawLevelField(x, y);
9817 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9823 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9825 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9826 printf("GameActions(): This should never happen!\n");
9828 ChangePage[x][y] = -1;
9833 if (WasJustMoving[x][y] > 0)
9834 WasJustMoving[x][y]--;
9835 if (WasJustFalling[x][y] > 0)
9836 WasJustFalling[x][y]--;
9837 if (CheckCollision[x][y] > 0)
9838 CheckCollision[x][y]--;
9842 /* reset finished pushing action (not done in ContinueMoving() to allow
9843 continuous pushing animation for elements with zero push delay) */
9844 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9846 ResetGfxAnimation(x, y);
9847 DrawLevelField(x, y);
9851 if (IS_BLOCKED(x, y))
9855 Blocked2Moving(x, y, &oldx, &oldy);
9856 if (!IS_MOVING(oldx, oldy))
9858 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9859 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9860 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9861 printf("GameActions(): This should never happen!\n");
9868 SCAN_PLAYFIELD(x, y)
9870 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9873 element = Feld[x][y];
9874 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9877 printf("::: %d,%d\n", x, y);
9879 if (element == EL_ROCK)
9880 printf("::: Yo man! Rocks can fall!\n");
9884 ResetGfxFrame(x, y, TRUE);
9886 if (graphic_info[graphic].anim_global_sync)
9887 GfxFrame[x][y] = FrameCounter;
9888 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9890 int old_gfx_frame = GfxFrame[x][y];
9892 GfxFrame[x][y] = CustomValue[x][y];
9895 if (GfxFrame[x][y] != old_gfx_frame)
9897 DrawLevelGraphicAnimation(x, y, graphic);
9899 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9901 int old_gfx_frame = GfxFrame[x][y];
9903 GfxFrame[x][y] = element_info[element].collect_score;
9906 if (GfxFrame[x][y] != old_gfx_frame)
9908 DrawLevelGraphicAnimation(x, y, graphic);
9910 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9912 int old_gfx_frame = GfxFrame[x][y];
9914 GfxFrame[x][y] = ChangeDelay[x][y];
9917 if (GfxFrame[x][y] != old_gfx_frame)
9919 DrawLevelGraphicAnimation(x, y, graphic);
9923 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9924 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9925 ResetRandomAnimationValue(x, y);
9927 SetRandomAnimationValue(x, y);
9929 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9931 if (IS_INACTIVE(element))
9933 if (IS_ANIMATED(graphic))
9934 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9939 /* this may take place after moving, so 'element' may have changed */
9940 if (IS_CHANGING(x, y) &&
9941 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9943 int page = element_info[element].event_page_nr[CE_DELAY];
9945 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9949 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9953 if (element == EL_CUSTOM_255)
9954 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9958 HandleElementChange(x, y, page);
9960 if (CAN_CHANGE(element))
9961 HandleElementChange(x, y, page);
9963 if (HAS_ACTION(element))
9964 ExecuteCustomElementAction(x, y, element, page);
9969 element = Feld[x][y];
9970 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9973 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9977 element = Feld[x][y];
9978 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9980 if (IS_ANIMATED(graphic) &&
9983 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9985 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9986 EdelsteinFunkeln(x, y);
9988 else if ((element == EL_ACID ||
9989 element == EL_EXIT_OPEN ||
9990 element == EL_SP_EXIT_OPEN ||
9991 element == EL_SP_TERMINAL ||
9992 element == EL_SP_TERMINAL_ACTIVE ||
9993 element == EL_EXTRA_TIME ||
9994 element == EL_SHIELD_NORMAL ||
9995 element == EL_SHIELD_DEADLY) &&
9996 IS_ANIMATED(graphic))
9997 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9998 else if (IS_MOVING(x, y))
9999 ContinueMoving(x, y);
10000 else if (IS_ACTIVE_BOMB(element))
10001 CheckDynamite(x, y);
10002 else if (element == EL_AMOEBA_GROWING)
10003 AmoebeWaechst(x, y);
10004 else if (element == EL_AMOEBA_SHRINKING)
10005 AmoebaDisappearing(x, y);
10007 #if !USE_NEW_AMOEBA_CODE
10008 else if (IS_AMOEBALIVE(element))
10009 AmoebeAbleger(x, y);
10012 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10014 else if (element == EL_EXIT_CLOSED)
10016 else if (element == EL_SP_EXIT_CLOSED)
10018 else if (element == EL_EXPANDABLE_WALL_GROWING)
10019 MauerWaechst(x, y);
10020 else if (element == EL_EXPANDABLE_WALL ||
10021 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10022 element == EL_EXPANDABLE_WALL_VERTICAL ||
10023 element == EL_EXPANDABLE_WALL_ANY)
10024 MauerAbleger(x, y);
10025 else if (element == EL_FLAMES)
10026 CheckForDragon(x, y);
10027 else if (element == EL_EXPLOSION)
10028 ; /* drawing of correct explosion animation is handled separately */
10029 else if (element == EL_ELEMENT_SNAPPING ||
10030 element == EL_DIAGONAL_SHRINKING ||
10031 element == EL_DIAGONAL_GROWING)
10034 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10036 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10039 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10040 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10043 if (element == EL_CUSTOM_255 ||
10044 element == EL_CUSTOM_256)
10045 DrawLevelGraphicAnimation(x, y, graphic);
10048 if (IS_BELT_ACTIVE(element))
10049 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10051 if (game.magic_wall_active)
10053 int jx = local_player->jx, jy = local_player->jy;
10055 /* play the element sound at the position nearest to the player */
10056 if ((element == EL_MAGIC_WALL_FULL ||
10057 element == EL_MAGIC_WALL_ACTIVE ||
10058 element == EL_MAGIC_WALL_EMPTYING ||
10059 element == EL_BD_MAGIC_WALL_FULL ||
10060 element == EL_BD_MAGIC_WALL_ACTIVE ||
10061 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10062 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10070 #if USE_NEW_AMOEBA_CODE
10071 /* new experimental amoeba growth stuff */
10072 if (!(FrameCounter % 8))
10074 static unsigned long random = 1684108901;
10076 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10078 x = RND(lev_fieldx);
10079 y = RND(lev_fieldy);
10080 element = Feld[x][y];
10082 if (!IS_PLAYER(x,y) &&
10083 (element == EL_EMPTY ||
10084 CAN_GROW_INTO(element) ||
10085 element == EL_QUICKSAND_EMPTY ||
10086 element == EL_ACID_SPLASH_LEFT ||
10087 element == EL_ACID_SPLASH_RIGHT))
10089 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10090 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10091 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10092 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10093 Feld[x][y] = EL_AMOEBA_DROP;
10096 random = random * 129 + 1;
10102 if (game.explosions_delayed)
10105 game.explosions_delayed = FALSE;
10108 SCAN_PLAYFIELD(x, y)
10110 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10113 element = Feld[x][y];
10115 if (ExplodeField[x][y])
10116 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10117 else if (element == EL_EXPLOSION)
10118 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10120 ExplodeField[x][y] = EX_TYPE_NONE;
10123 game.explosions_delayed = TRUE;
10126 if (game.magic_wall_active)
10128 if (!(game.magic_wall_time_left % 4))
10130 int element = Feld[magic_wall_x][magic_wall_y];
10132 if (element == EL_BD_MAGIC_WALL_FULL ||
10133 element == EL_BD_MAGIC_WALL_ACTIVE ||
10134 element == EL_BD_MAGIC_WALL_EMPTYING)
10135 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10137 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10140 if (game.magic_wall_time_left > 0)
10142 game.magic_wall_time_left--;
10143 if (!game.magic_wall_time_left)
10146 SCAN_PLAYFIELD(x, y)
10148 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10151 element = Feld[x][y];
10153 if (element == EL_MAGIC_WALL_ACTIVE ||
10154 element == EL_MAGIC_WALL_FULL)
10156 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10157 DrawLevelField(x, y);
10159 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10160 element == EL_BD_MAGIC_WALL_FULL)
10162 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10163 DrawLevelField(x, y);
10167 game.magic_wall_active = FALSE;
10172 if (game.light_time_left > 0)
10174 game.light_time_left--;
10176 if (game.light_time_left == 0)
10177 RedrawAllLightSwitchesAndInvisibleElements();
10180 if (game.timegate_time_left > 0)
10182 game.timegate_time_left--;
10184 if (game.timegate_time_left == 0)
10185 CloseAllOpenTimegates();
10188 if (game.lenses_time_left > 0)
10190 game.lenses_time_left--;
10192 if (game.lenses_time_left == 0)
10193 RedrawAllInvisibleElementsForLenses();
10196 if (game.magnify_time_left > 0)
10198 game.magnify_time_left--;
10200 if (game.magnify_time_left == 0)
10201 RedrawAllInvisibleElementsForMagnifier();
10204 for (i = 0; i < MAX_PLAYERS; i++)
10206 struct PlayerInfo *player = &stored_player[i];
10208 if (SHIELD_ON(player))
10210 if (player->shield_deadly_time_left)
10211 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10212 else if (player->shield_normal_time_left)
10213 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10220 PlayAllPlayersSound();
10222 if (options.debug) /* calculate frames per second */
10224 static unsigned long fps_counter = 0;
10225 static int fps_frames = 0;
10226 unsigned long fps_delay_ms = Counter() - fps_counter;
10230 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10232 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10235 fps_counter = Counter();
10238 redraw_mask |= REDRAW_FPS;
10241 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10243 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10245 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10247 local_player->show_envelope = 0;
10250 /* use random number generator in every frame to make it less predictable */
10251 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10255 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10257 int min_x = x, min_y = y, max_x = x, max_y = y;
10260 for (i = 0; i < MAX_PLAYERS; i++)
10262 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10264 if (!stored_player[i].active || &stored_player[i] == player)
10267 min_x = MIN(min_x, jx);
10268 min_y = MIN(min_y, jy);
10269 max_x = MAX(max_x, jx);
10270 max_y = MAX(max_y, jy);
10273 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10276 static boolean AllPlayersInVisibleScreen()
10280 for (i = 0; i < MAX_PLAYERS; i++)
10282 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10284 if (!stored_player[i].active)
10287 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10294 void ScrollLevel(int dx, int dy)
10296 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10299 BlitBitmap(drawto_field, drawto_field,
10300 FX + TILEX * (dx == -1) - softscroll_offset,
10301 FY + TILEY * (dy == -1) - softscroll_offset,
10302 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10303 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10304 FX + TILEX * (dx == 1) - softscroll_offset,
10305 FY + TILEY * (dy == 1) - softscroll_offset);
10309 x = (dx == 1 ? BX1 : BX2);
10310 for (y = BY1; y <= BY2; y++)
10311 DrawScreenField(x, y);
10316 y = (dy == 1 ? BY1 : BY2);
10317 for (x = BX1; x <= BX2; x++)
10318 DrawScreenField(x, y);
10321 redraw_mask |= REDRAW_FIELD;
10324 static boolean canFallDown(struct PlayerInfo *player)
10326 int jx = player->jx, jy = player->jy;
10328 return (IN_LEV_FIELD(jx, jy + 1) &&
10329 (IS_FREE(jx, jy + 1) ||
10330 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10331 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10332 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10335 static boolean canPassField(int x, int y, int move_dir)
10337 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10338 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10339 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10340 int nextx = x + dx;
10341 int nexty = y + dy;
10342 int element = Feld[x][y];
10344 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10345 !CAN_MOVE(element) &&
10346 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10347 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10348 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10351 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10353 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10354 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10355 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10359 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10360 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10361 (IS_DIGGABLE(Feld[newx][newy]) ||
10362 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10363 canPassField(newx, newy, move_dir)));
10366 static void CheckGravityMovement(struct PlayerInfo *player)
10368 #if USE_PLAYER_GRAVITY
10369 if (player->gravity && !player->programmed_action)
10371 if (game.gravity && !player->programmed_action)
10374 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10375 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10376 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10377 int jx = player->jx, jy = player->jy;
10378 boolean player_is_moving_to_valid_field =
10379 (!player_is_snapping &&
10380 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10381 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10382 boolean player_can_fall_down = canFallDown(player);
10384 if (player_can_fall_down &&
10385 !player_is_moving_to_valid_field)
10386 player->programmed_action = MV_DOWN;
10390 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10392 return CheckGravityMovement(player);
10394 #if USE_PLAYER_GRAVITY
10395 if (player->gravity && !player->programmed_action)
10397 if (game.gravity && !player->programmed_action)
10400 int jx = player->jx, jy = player->jy;
10401 boolean field_under_player_is_free =
10402 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10403 boolean player_is_standing_on_valid_field =
10404 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10405 (IS_WALKABLE(Feld[jx][jy]) &&
10406 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10408 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10409 player->programmed_action = MV_DOWN;
10414 MovePlayerOneStep()
10415 -----------------------------------------------------------------------------
10416 dx, dy: direction (non-diagonal) to try to move the player to
10417 real_dx, real_dy: direction as read from input device (can be diagonal)
10420 boolean MovePlayerOneStep(struct PlayerInfo *player,
10421 int dx, int dy, int real_dx, int real_dy)
10423 int jx = player->jx, jy = player->jy;
10424 int new_jx = jx + dx, new_jy = jy + dy;
10425 #if !USE_FIXED_DONT_RUN_INTO
10429 boolean player_can_move = !player->cannot_move;
10431 if (!player->active || (!dx && !dy))
10432 return MP_NO_ACTION;
10434 player->MovDir = (dx < 0 ? MV_LEFT :
10435 dx > 0 ? MV_RIGHT :
10437 dy > 0 ? MV_DOWN : MV_NONE);
10439 if (!IN_LEV_FIELD(new_jx, new_jy))
10440 return MP_NO_ACTION;
10442 if (!player_can_move)
10445 if (player->MovPos == 0)
10447 player->is_moving = FALSE;
10448 player->is_digging = FALSE;
10449 player->is_collecting = FALSE;
10450 player->is_snapping = FALSE;
10451 player->is_pushing = FALSE;
10454 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10455 SnapField(player, 0, 0);
10459 return MP_NO_ACTION;
10464 if (!options.network && game.centered_player_nr == -1 &&
10465 !AllPlayersInSight(player, new_jx, new_jy))
10466 return MP_NO_ACTION;
10468 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10469 return MP_NO_ACTION;
10472 #if !USE_FIXED_DONT_RUN_INTO
10473 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10475 /* (moved to DigField()) */
10476 if (player_can_move && DONT_RUN_INTO(element))
10478 if (element == EL_ACID && dx == 0 && dy == 1)
10480 SplashAcid(new_jx, new_jy);
10481 Feld[jx][jy] = EL_PLAYER_1;
10482 InitMovingField(jx, jy, MV_DOWN);
10483 Store[jx][jy] = EL_ACID;
10484 ContinueMoving(jx, jy);
10485 BuryPlayer(player);
10488 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10494 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10496 #if USE_FIXED_DONT_RUN_INTO
10497 if (can_move == MP_DONT_RUN_INTO)
10501 if (can_move != MP_MOVING)
10504 #if USE_FIXED_DONT_RUN_INTO
10507 /* check if DigField() has caused relocation of the player */
10508 if (player->jx != jx || player->jy != jy)
10509 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10511 StorePlayer[jx][jy] = 0;
10512 player->last_jx = jx;
10513 player->last_jy = jy;
10514 player->jx = new_jx;
10515 player->jy = new_jy;
10516 StorePlayer[new_jx][new_jy] = player->element_nr;
10518 if (player->move_delay_value_next != -1)
10520 player->move_delay_value = player->move_delay_value_next;
10521 player->move_delay_value_next = -1;
10525 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10527 player->step_counter++;
10529 PlayerVisit[jx][jy] = FrameCounter;
10531 ScrollPlayer(player, SCROLL_INIT);
10536 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10538 int jx = player->jx, jy = player->jy;
10539 int old_jx = jx, old_jy = jy;
10540 int moved = MP_NO_ACTION;
10542 if (!player->active)
10547 if (player->MovPos == 0)
10549 player->is_moving = FALSE;
10550 player->is_digging = FALSE;
10551 player->is_collecting = FALSE;
10552 player->is_snapping = FALSE;
10553 player->is_pushing = FALSE;
10559 if (player->move_delay > 0)
10562 player->move_delay = -1; /* set to "uninitialized" value */
10564 /* store if player is automatically moved to next field */
10565 player->is_auto_moving = (player->programmed_action != MV_NONE);
10567 /* remove the last programmed player action */
10568 player->programmed_action = 0;
10570 if (player->MovPos)
10572 /* should only happen if pre-1.2 tape recordings are played */
10573 /* this is only for backward compatibility */
10575 int original_move_delay_value = player->move_delay_value;
10578 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10582 /* scroll remaining steps with finest movement resolution */
10583 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10585 while (player->MovPos)
10587 ScrollPlayer(player, SCROLL_GO_ON);
10588 ScrollScreen(NULL, SCROLL_GO_ON);
10590 AdvanceFrameAndPlayerCounters(player->index_nr);
10596 player->move_delay_value = original_move_delay_value;
10599 player->is_active = FALSE;
10601 if (player->last_move_dir & MV_HORIZONTAL)
10603 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10604 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10608 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10609 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10612 #if USE_FIXED_BORDER_RUNNING_GFX
10613 if (!moved && !player->is_active)
10615 player->is_moving = FALSE;
10616 player->is_digging = FALSE;
10617 player->is_collecting = FALSE;
10618 player->is_snapping = FALSE;
10619 player->is_pushing = FALSE;
10627 if (moved & MP_MOVING && !ScreenMovPos &&
10628 (player->index_nr == game.centered_player_nr ||
10629 game.centered_player_nr == -1))
10631 if (moved & MP_MOVING && !ScreenMovPos &&
10632 (player == local_player || !options.network))
10635 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10636 int offset = (setup.scroll_delay ? 3 : 0);
10638 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10640 /* actual player has left the screen -- scroll in that direction */
10641 if (jx != old_jx) /* player has moved horizontally */
10642 scroll_x += (jx - old_jx);
10643 else /* player has moved vertically */
10644 scroll_y += (jy - old_jy);
10648 if (jx != old_jx) /* player has moved horizontally */
10650 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10651 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10652 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10654 /* don't scroll over playfield boundaries */
10655 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10656 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10658 /* don't scroll more than one field at a time */
10659 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10661 /* don't scroll against the player's moving direction */
10662 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10663 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10664 scroll_x = old_scroll_x;
10666 else /* player has moved vertically */
10668 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10669 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10670 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10672 /* don't scroll over playfield boundaries */
10673 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10674 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10676 /* don't scroll more than one field at a time */
10677 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10679 /* don't scroll against the player's moving direction */
10680 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10681 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10682 scroll_y = old_scroll_y;
10686 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10689 if (!options.network && game.centered_player_nr == -1 &&
10690 !AllPlayersInVisibleScreen())
10692 scroll_x = old_scroll_x;
10693 scroll_y = old_scroll_y;
10697 if (!options.network && !AllPlayersInVisibleScreen())
10699 scroll_x = old_scroll_x;
10700 scroll_y = old_scroll_y;
10705 ScrollScreen(player, SCROLL_INIT);
10706 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10711 player->StepFrame = 0;
10713 if (moved & MP_MOVING)
10715 if (old_jx != jx && old_jy == jy)
10716 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10717 else if (old_jx == jx && old_jy != jy)
10718 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10720 DrawLevelField(jx, jy); /* for "crumbled sand" */
10722 player->last_move_dir = player->MovDir;
10723 player->is_moving = TRUE;
10724 player->is_snapping = FALSE;
10725 player->is_switching = FALSE;
10726 player->is_dropping = FALSE;
10727 player->is_dropping_pressed = FALSE;
10728 player->drop_pressed_delay = 0;
10732 CheckGravityMovementWhenNotMoving(player);
10734 player->is_moving = FALSE;
10736 /* at this point, the player is allowed to move, but cannot move right now
10737 (e.g. because of something blocking the way) -- ensure that the player
10738 is also allowed to move in the next frame (in old versions before 3.1.1,
10739 the player was forced to wait again for eight frames before next try) */
10741 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10742 player->move_delay = 0; /* allow direct movement in the next frame */
10745 if (player->move_delay == -1) /* not yet initialized by DigField() */
10746 player->move_delay = player->move_delay_value;
10748 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10750 TestIfPlayerTouchesBadThing(jx, jy);
10751 TestIfPlayerTouchesCustomElement(jx, jy);
10754 if (!player->active)
10755 RemovePlayer(player);
10760 void ScrollPlayer(struct PlayerInfo *player, int mode)
10762 int jx = player->jx, jy = player->jy;
10763 int last_jx = player->last_jx, last_jy = player->last_jy;
10764 int move_stepsize = TILEX / player->move_delay_value;
10766 #if USE_NEW_PLAYER_SPEED
10767 if (!player->active)
10770 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10773 if (!player->active || player->MovPos == 0)
10777 if (mode == SCROLL_INIT)
10779 player->actual_frame_counter = FrameCounter;
10780 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10782 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10783 Feld[last_jx][last_jy] == EL_EMPTY)
10785 int last_field_block_delay = 0; /* start with no blocking at all */
10786 int block_delay_adjustment = player->block_delay_adjustment;
10788 /* if player blocks last field, add delay for exactly one move */
10789 if (player->block_last_field)
10791 last_field_block_delay += player->move_delay_value;
10793 /* when blocking enabled, prevent moving up despite gravity */
10794 #if USE_PLAYER_GRAVITY
10795 if (player->gravity && player->MovDir == MV_UP)
10796 block_delay_adjustment = -1;
10798 if (game.gravity && player->MovDir == MV_UP)
10799 block_delay_adjustment = -1;
10803 /* add block delay adjustment (also possible when not blocking) */
10804 last_field_block_delay += block_delay_adjustment;
10806 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10807 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10810 #if USE_NEW_PLAYER_SPEED
10811 if (player->MovPos != 0) /* player has not yet reached destination */
10817 else if (!FrameReached(&player->actual_frame_counter, 1))
10821 printf("::: player->MovPos: %d -> %d\n",
10823 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10826 #if USE_NEW_PLAYER_SPEED
10827 if (player->MovPos != 0)
10829 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10830 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10832 /* before DrawPlayer() to draw correct player graphic for this case */
10833 if (player->MovPos == 0)
10834 CheckGravityMovement(player);
10837 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10838 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10840 /* before DrawPlayer() to draw correct player graphic for this case */
10841 if (player->MovPos == 0)
10842 CheckGravityMovement(player);
10845 if (player->MovPos == 0) /* player reached destination field */
10848 printf("::: player reached destination field\n");
10851 if (player->move_delay_reset_counter > 0)
10853 player->move_delay_reset_counter--;
10855 if (player->move_delay_reset_counter == 0)
10857 /* continue with normal speed after quickly moving through gate */
10858 HALVE_PLAYER_SPEED(player);
10860 /* be able to make the next move without delay */
10861 player->move_delay = 0;
10865 player->last_jx = jx;
10866 player->last_jy = jy;
10868 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10869 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10870 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10872 DrawPlayer(player); /* needed here only to cleanup last field */
10873 RemovePlayer(player);
10875 if (local_player->friends_still_needed == 0 ||
10876 IS_SP_ELEMENT(Feld[jx][jy]))
10877 player->LevelSolved = player->GameOver = TRUE;
10880 /* this breaks one level: "machine", level 000 */
10882 int move_direction = player->MovDir;
10883 int enter_side = MV_DIR_OPPOSITE(move_direction);
10884 int leave_side = move_direction;
10885 int old_jx = last_jx;
10886 int old_jy = last_jy;
10887 int old_element = Feld[old_jx][old_jy];
10888 int new_element = Feld[jx][jy];
10890 if (IS_CUSTOM_ELEMENT(old_element))
10891 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10893 player->index_bit, leave_side);
10895 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10896 CE_PLAYER_LEAVES_X,
10897 player->index_bit, leave_side);
10899 if (IS_CUSTOM_ELEMENT(new_element))
10900 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10901 player->index_bit, enter_side);
10903 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10904 CE_PLAYER_ENTERS_X,
10905 player->index_bit, enter_side);
10907 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10908 CE_MOVE_OF_X, move_direction);
10911 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10913 TestIfPlayerTouchesBadThing(jx, jy);
10914 TestIfPlayerTouchesCustomElement(jx, jy);
10916 /* needed because pushed element has not yet reached its destination,
10917 so it would trigger a change event at its previous field location */
10918 if (!player->is_pushing)
10919 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10921 if (!player->active)
10922 RemovePlayer(player);
10925 if (level.use_step_counter)
10935 if (TimeLeft <= 10 && setup.time_limit)
10936 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10938 DrawGameValue_Time(TimeLeft);
10940 if (!TimeLeft && setup.time_limit)
10941 for (i = 0; i < MAX_PLAYERS; i++)
10942 KillPlayer(&stored_player[i]);
10944 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10945 DrawGameValue_Time(TimePlayed);
10948 if (tape.single_step && tape.recording && !tape.pausing &&
10949 !player->programmed_action)
10950 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10954 void ScrollScreen(struct PlayerInfo *player, int mode)
10956 static unsigned long screen_frame_counter = 0;
10958 if (mode == SCROLL_INIT)
10960 /* set scrolling step size according to actual player's moving speed */
10961 ScrollStepSize = TILEX / player->move_delay_value;
10963 screen_frame_counter = FrameCounter;
10964 ScreenMovDir = player->MovDir;
10965 ScreenMovPos = player->MovPos;
10966 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10969 else if (!FrameReached(&screen_frame_counter, 1))
10974 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10975 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10976 redraw_mask |= REDRAW_FIELD;
10979 ScreenMovDir = MV_NONE;
10982 void TestIfPlayerTouchesCustomElement(int x, int y)
10984 static int xy[4][2] =
10991 static int trigger_sides[4][2] =
10993 /* center side border side */
10994 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10995 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10996 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10997 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10999 static int touch_dir[4] =
11001 MV_LEFT | MV_RIGHT,
11006 int center_element = Feld[x][y]; /* should always be non-moving! */
11009 for (i = 0; i < NUM_DIRECTIONS; i++)
11011 int xx = x + xy[i][0];
11012 int yy = y + xy[i][1];
11013 int center_side = trigger_sides[i][0];
11014 int border_side = trigger_sides[i][1];
11015 int border_element;
11017 if (!IN_LEV_FIELD(xx, yy))
11020 if (IS_PLAYER(x, y))
11022 struct PlayerInfo *player = PLAYERINFO(x, y);
11024 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11025 border_element = Feld[xx][yy]; /* may be moving! */
11026 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11027 border_element = Feld[xx][yy];
11028 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11029 border_element = MovingOrBlocked2Element(xx, yy);
11031 continue; /* center and border element do not touch */
11033 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11034 player->index_bit, border_side);
11035 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11036 CE_PLAYER_TOUCHES_X,
11037 player->index_bit, border_side);
11039 else if (IS_PLAYER(xx, yy))
11041 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11043 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11045 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11046 continue; /* center and border element do not touch */
11049 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11050 player->index_bit, center_side);
11051 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11052 CE_PLAYER_TOUCHES_X,
11053 player->index_bit, center_side);
11059 #if USE_ELEMENT_TOUCHING_BUGFIX
11061 void TestIfElementTouchesCustomElement(int x, int y)
11063 static int xy[4][2] =
11070 static int trigger_sides[4][2] =
11072 /* center side border side */
11073 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11074 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11075 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11076 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11078 static int touch_dir[4] =
11080 MV_LEFT | MV_RIGHT,
11085 boolean change_center_element = FALSE;
11086 int center_element = Feld[x][y]; /* should always be non-moving! */
11087 int border_element_old[NUM_DIRECTIONS];
11090 for (i = 0; i < NUM_DIRECTIONS; i++)
11092 int xx = x + xy[i][0];
11093 int yy = y + xy[i][1];
11094 int border_element;
11096 border_element_old[i] = -1;
11098 if (!IN_LEV_FIELD(xx, yy))
11101 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11102 border_element = Feld[xx][yy]; /* may be moving! */
11103 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11104 border_element = Feld[xx][yy];
11105 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11106 border_element = MovingOrBlocked2Element(xx, yy);
11108 continue; /* center and border element do not touch */
11110 border_element_old[i] = border_element;
11113 for (i = 0; i < NUM_DIRECTIONS; i++)
11115 int xx = x + xy[i][0];
11116 int yy = y + xy[i][1];
11117 int center_side = trigger_sides[i][0];
11118 int border_element = border_element_old[i];
11120 if (border_element == -1)
11123 /* check for change of border element */
11124 CheckElementChangeBySide(xx, yy, border_element, center_element,
11125 CE_TOUCHING_X, center_side);
11128 for (i = 0; i < NUM_DIRECTIONS; i++)
11130 int border_side = trigger_sides[i][1];
11131 int border_element = border_element_old[i];
11133 if (border_element == -1)
11136 /* check for change of center element (but change it only once) */
11137 if (!change_center_element)
11138 change_center_element =
11139 CheckElementChangeBySide(x, y, center_element, border_element,
11140 CE_TOUCHING_X, border_side);
11146 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11148 static int xy[4][2] =
11155 static int trigger_sides[4][2] =
11157 /* center side border side */
11158 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11159 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11160 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11161 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11163 static int touch_dir[4] =
11165 MV_LEFT | MV_RIGHT,
11170 boolean change_center_element = FALSE;
11171 int center_element = Feld[x][y]; /* should always be non-moving! */
11174 for (i = 0; i < NUM_DIRECTIONS; i++)
11176 int xx = x + xy[i][0];
11177 int yy = y + xy[i][1];
11178 int center_side = trigger_sides[i][0];
11179 int border_side = trigger_sides[i][1];
11180 int border_element;
11182 if (!IN_LEV_FIELD(xx, yy))
11185 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11186 border_element = Feld[xx][yy]; /* may be moving! */
11187 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11188 border_element = Feld[xx][yy];
11189 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11190 border_element = MovingOrBlocked2Element(xx, yy);
11192 continue; /* center and border element do not touch */
11194 /* check for change of center element (but change it only once) */
11195 if (!change_center_element)
11196 change_center_element =
11197 CheckElementChangeBySide(x, y, center_element, border_element,
11198 CE_TOUCHING_X, border_side);
11200 /* check for change of border element */
11201 CheckElementChangeBySide(xx, yy, border_element, center_element,
11202 CE_TOUCHING_X, center_side);
11208 void TestIfElementHitsCustomElement(int x, int y, int direction)
11210 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11211 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11212 int hitx = x + dx, hity = y + dy;
11213 int hitting_element = Feld[x][y];
11214 int touched_element;
11216 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11219 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11220 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11222 if (IN_LEV_FIELD(hitx, hity))
11224 int opposite_direction = MV_DIR_OPPOSITE(direction);
11225 int hitting_side = direction;
11226 int touched_side = opposite_direction;
11227 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11228 MovDir[hitx][hity] != direction ||
11229 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11235 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11236 CE_HITTING_X, touched_side);
11238 CheckElementChangeBySide(hitx, hity, touched_element,
11239 hitting_element, CE_HIT_BY_X, hitting_side);
11241 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11242 CE_HIT_BY_SOMETHING, opposite_direction);
11246 /* "hitting something" is also true when hitting the playfield border */
11247 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11248 CE_HITTING_SOMETHING, direction);
11252 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11254 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11255 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11256 int hitx = x + dx, hity = y + dy;
11257 int hitting_element = Feld[x][y];
11258 int touched_element;
11260 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11261 !IS_FREE(hitx, hity) &&
11262 (!IS_MOVING(hitx, hity) ||
11263 MovDir[hitx][hity] != direction ||
11264 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11267 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11271 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11275 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11276 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11278 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11279 EP_CAN_SMASH_EVERYTHING, direction);
11281 if (IN_LEV_FIELD(hitx, hity))
11283 int opposite_direction = MV_DIR_OPPOSITE(direction);
11284 int hitting_side = direction;
11285 int touched_side = opposite_direction;
11287 int touched_element = MovingOrBlocked2Element(hitx, hity);
11290 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11291 MovDir[hitx][hity] != direction ||
11292 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11301 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11302 CE_SMASHED_BY_SOMETHING, opposite_direction);
11304 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11305 CE_OTHER_IS_SMASHING, touched_side);
11307 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11308 CE_OTHER_GETS_SMASHED, hitting_side);
11314 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11316 int i, kill_x = -1, kill_y = -1;
11318 int bad_element = -1;
11319 static int test_xy[4][2] =
11326 static int test_dir[4] =
11334 for (i = 0; i < NUM_DIRECTIONS; i++)
11336 int test_x, test_y, test_move_dir, test_element;
11338 test_x = good_x + test_xy[i][0];
11339 test_y = good_y + test_xy[i][1];
11341 if (!IN_LEV_FIELD(test_x, test_y))
11345 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11347 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11349 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11350 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11352 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11353 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11357 bad_element = test_element;
11363 if (kill_x != -1 || kill_y != -1)
11365 if (IS_PLAYER(good_x, good_y))
11367 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11369 if (player->shield_deadly_time_left > 0 &&
11370 !IS_INDESTRUCTIBLE(bad_element))
11371 Bang(kill_x, kill_y);
11372 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11373 KillPlayer(player);
11376 Bang(good_x, good_y);
11380 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11382 int i, kill_x = -1, kill_y = -1;
11383 int bad_element = Feld[bad_x][bad_y];
11384 static int test_xy[4][2] =
11391 static int touch_dir[4] =
11393 MV_LEFT | MV_RIGHT,
11398 static int test_dir[4] =
11406 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11409 for (i = 0; i < NUM_DIRECTIONS; i++)
11411 int test_x, test_y, test_move_dir, test_element;
11413 test_x = bad_x + test_xy[i][0];
11414 test_y = bad_y + test_xy[i][1];
11415 if (!IN_LEV_FIELD(test_x, test_y))
11419 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11421 test_element = Feld[test_x][test_y];
11423 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11424 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11426 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11427 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11429 /* good thing is player or penguin that does not move away */
11430 if (IS_PLAYER(test_x, test_y))
11432 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11434 if (bad_element == EL_ROBOT && player->is_moving)
11435 continue; /* robot does not kill player if he is moving */
11437 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11439 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11440 continue; /* center and border element do not touch */
11447 else if (test_element == EL_PENGUIN)
11456 if (kill_x != -1 || kill_y != -1)
11458 if (IS_PLAYER(kill_x, kill_y))
11460 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11462 if (player->shield_deadly_time_left > 0 &&
11463 !IS_INDESTRUCTIBLE(bad_element))
11464 Bang(bad_x, bad_y);
11465 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11466 KillPlayer(player);
11469 Bang(kill_x, kill_y);
11473 void TestIfPlayerTouchesBadThing(int x, int y)
11475 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11478 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11480 TestIfGoodThingHitsBadThing(x, y, move_dir);
11483 void TestIfBadThingTouchesPlayer(int x, int y)
11485 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11488 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11490 TestIfBadThingHitsGoodThing(x, y, move_dir);
11493 void TestIfFriendTouchesBadThing(int x, int y)
11495 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11498 void TestIfBadThingTouchesFriend(int x, int y)
11500 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11503 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11505 int i, kill_x = bad_x, kill_y = bad_y;
11506 static int xy[4][2] =
11514 for (i = 0; i < NUM_DIRECTIONS; i++)
11518 x = bad_x + xy[i][0];
11519 y = bad_y + xy[i][1];
11520 if (!IN_LEV_FIELD(x, y))
11523 element = Feld[x][y];
11524 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11525 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11533 if (kill_x != bad_x || kill_y != bad_y)
11534 Bang(bad_x, bad_y);
11537 void KillPlayer(struct PlayerInfo *player)
11539 int jx = player->jx, jy = player->jy;
11541 if (!player->active)
11544 /* remove accessible field at the player's position */
11545 Feld[jx][jy] = EL_EMPTY;
11547 /* deactivate shield (else Bang()/Explode() would not work right) */
11548 player->shield_normal_time_left = 0;
11549 player->shield_deadly_time_left = 0;
11552 BuryPlayer(player);
11555 static void KillPlayerUnlessEnemyProtected(int x, int y)
11557 if (!PLAYER_ENEMY_PROTECTED(x, y))
11558 KillPlayer(PLAYERINFO(x, y));
11561 static void KillPlayerUnlessExplosionProtected(int x, int y)
11563 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11564 KillPlayer(PLAYERINFO(x, y));
11567 void BuryPlayer(struct PlayerInfo *player)
11569 int jx = player->jx, jy = player->jy;
11571 if (!player->active)
11574 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11575 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11577 player->GameOver = TRUE;
11578 RemovePlayer(player);
11581 void RemovePlayer(struct PlayerInfo *player)
11583 int jx = player->jx, jy = player->jy;
11584 int i, found = FALSE;
11586 player->present = FALSE;
11587 player->active = FALSE;
11589 if (!ExplodeField[jx][jy])
11590 StorePlayer[jx][jy] = 0;
11592 if (player->is_moving)
11593 DrawLevelField(player->last_jx, player->last_jy);
11595 for (i = 0; i < MAX_PLAYERS; i++)
11596 if (stored_player[i].active)
11600 AllPlayersGone = TRUE;
11606 #if USE_NEW_SNAP_DELAY
11607 static void setFieldForSnapping(int x, int y, int element, int direction)
11609 struct ElementInfo *ei = &element_info[element];
11610 int direction_bit = MV_DIR_TO_BIT(direction);
11611 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11612 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11613 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11615 Feld[x][y] = EL_ELEMENT_SNAPPING;
11616 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11618 ResetGfxAnimation(x, y);
11620 GfxElement[x][y] = element;
11621 GfxAction[x][y] = action;
11622 GfxDir[x][y] = direction;
11623 GfxFrame[x][y] = -1;
11628 =============================================================================
11629 checkDiagonalPushing()
11630 -----------------------------------------------------------------------------
11631 check if diagonal input device direction results in pushing of object
11632 (by checking if the alternative direction is walkable, diggable, ...)
11633 =============================================================================
11636 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11637 int x, int y, int real_dx, int real_dy)
11639 int jx, jy, dx, dy, xx, yy;
11641 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11644 /* diagonal direction: check alternative direction */
11649 xx = jx + (dx == 0 ? real_dx : 0);
11650 yy = jy + (dy == 0 ? real_dy : 0);
11652 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11656 =============================================================================
11658 -----------------------------------------------------------------------------
11659 x, y: field next to player (non-diagonal) to try to dig to
11660 real_dx, real_dy: direction as read from input device (can be diagonal)
11661 =============================================================================
11664 int DigField(struct PlayerInfo *player,
11665 int oldx, int oldy, int x, int y,
11666 int real_dx, int real_dy, int mode)
11668 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11669 boolean player_was_pushing = player->is_pushing;
11670 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11671 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11672 int jx = oldx, jy = oldy;
11673 int dx = x - jx, dy = y - jy;
11674 int nextx = x + dx, nexty = y + dy;
11675 int move_direction = (dx == -1 ? MV_LEFT :
11676 dx == +1 ? MV_RIGHT :
11678 dy == +1 ? MV_DOWN : MV_NONE);
11679 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11680 int dig_side = MV_DIR_OPPOSITE(move_direction);
11681 int old_element = Feld[jx][jy];
11682 #if USE_FIXED_DONT_RUN_INTO
11683 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11689 if (is_player) /* function can also be called by EL_PENGUIN */
11691 if (player->MovPos == 0)
11693 player->is_digging = FALSE;
11694 player->is_collecting = FALSE;
11697 if (player->MovPos == 0) /* last pushing move finished */
11698 player->is_pushing = FALSE;
11700 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11702 player->is_switching = FALSE;
11703 player->push_delay = -1;
11705 return MP_NO_ACTION;
11709 #if !USE_FIXED_DONT_RUN_INTO
11710 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11711 return MP_NO_ACTION;
11714 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11715 old_element = Back[jx][jy];
11717 /* in case of element dropped at player position, check background */
11718 else if (Back[jx][jy] != EL_EMPTY &&
11719 game.engine_version >= VERSION_IDENT(2,2,0,0))
11720 old_element = Back[jx][jy];
11722 /* checking here causes player to move into acid even if the current field
11723 cannot be left to that direction */
11725 #if USE_FIXED_DONT_RUN_INTO
11726 if (player_can_move && DONT_RUN_INTO(element))
11728 if (element == EL_ACID && dx == 0 && dy == 1)
11731 Feld[jx][jy] = EL_PLAYER_1;
11732 InitMovingField(jx, jy, MV_DOWN);
11733 Store[jx][jy] = EL_ACID;
11734 ContinueMoving(jx, jy);
11735 BuryPlayer(player);
11738 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11740 return MP_DONT_RUN_INTO;
11745 #if 1 /* ------------------------------ NEW ------------------------------ */
11747 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11748 return MP_NO_ACTION; /* field has no opening in this direction */
11750 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11751 return MP_NO_ACTION; /* field has no opening in this direction */
11753 #if USE_FIXED_DONT_RUN_INTO
11754 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11758 Feld[jx][jy] = player->artwork_element;
11760 Feld[jx][jy] = EL_PLAYER_1;
11762 InitMovingField(jx, jy, MV_DOWN);
11763 Store[jx][jy] = EL_ACID;
11764 ContinueMoving(jx, jy);
11765 BuryPlayer(player);
11767 return MP_DONT_RUN_INTO;
11771 #if USE_FIXED_DONT_RUN_INTO
11772 if (player_can_move && DONT_RUN_INTO(element))
11774 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11776 return MP_DONT_RUN_INTO;
11780 #else /* ------------------------------ OLD ------------------------------ */
11783 #if USE_FIXED_DONT_RUN_INTO
11784 if (player_can_move && DONT_RUN_INTO(element))
11786 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11788 return MP_DONT_RUN_INTO;
11793 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11794 return MP_NO_ACTION; /* field has no opening in this direction */
11796 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11797 return MP_NO_ACTION; /* field has no opening in this direction */
11799 /* checking here causes player to explode when moving into acid */
11801 #if USE_FIXED_DONT_RUN_INTO
11802 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11805 Feld[jx][jy] = EL_PLAYER_1;
11806 InitMovingField(jx, jy, MV_DOWN);
11807 Store[jx][jy] = EL_ACID;
11808 ContinueMoving(jx, jy);
11809 BuryPlayer(player);
11811 return MP_DONT_RUN_INTO;
11816 #endif /* ------------------------------ END ------------------------------ */
11819 #if USE_FIXED_DONT_RUN_INTO
11820 if (player_can_move && DONT_RUN_INTO(element))
11822 if (element == EL_ACID && dx == 0 && dy == 1)
11825 Feld[jx][jy] = EL_PLAYER_1;
11826 InitMovingField(jx, jy, MV_DOWN);
11827 Store[jx][jy] = EL_ACID;
11828 ContinueMoving(jx, jy);
11829 BuryPlayer(player);
11832 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11834 return MP_DONT_RUN_INTO;
11839 #if USE_FIXED_DONT_RUN_INTO
11840 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11841 return MP_NO_ACTION;
11844 #if !USE_FIXED_DONT_RUN_INTO
11845 element = Feld[x][y];
11848 collect_count = element_info[element].collect_count_initial;
11850 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11851 return MP_NO_ACTION;
11853 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11854 player_can_move = player_can_move_or_snap;
11856 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11857 game.engine_version >= VERSION_IDENT(2,2,0,0))
11859 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11860 player->index_bit, dig_side);
11861 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11862 player->index_bit, dig_side);
11864 if (Feld[x][y] != element) /* field changed by snapping */
11867 return MP_NO_ACTION;
11870 #if USE_PLAYER_GRAVITY
11871 if (player->gravity && is_player && !player->is_auto_moving &&
11872 canFallDown(player) && move_direction != MV_DOWN &&
11873 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11874 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11876 if (game.gravity && is_player && !player->is_auto_moving &&
11877 canFallDown(player) && move_direction != MV_DOWN &&
11878 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11879 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11882 if (player_can_move &&
11883 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11885 int sound_element = SND_ELEMENT(element);
11886 int sound_action = ACTION_WALKING;
11888 if (IS_RND_GATE(element))
11890 if (!player->key[RND_GATE_NR(element)])
11891 return MP_NO_ACTION;
11893 else if (IS_RND_GATE_GRAY(element))
11895 if (!player->key[RND_GATE_GRAY_NR(element)])
11896 return MP_NO_ACTION;
11898 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11900 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11901 return MP_NO_ACTION;
11903 else if (element == EL_EXIT_OPEN ||
11904 element == EL_SP_EXIT_OPEN ||
11905 element == EL_SP_EXIT_OPENING)
11907 sound_action = ACTION_PASSING; /* player is passing exit */
11909 else if (element == EL_EMPTY)
11911 sound_action = ACTION_MOVING; /* nothing to walk on */
11914 /* play sound from background or player, whatever is available */
11915 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11916 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11918 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11920 else if (player_can_move &&
11921 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11923 if (!ACCESS_FROM(element, opposite_direction))
11924 return MP_NO_ACTION; /* field not accessible from this direction */
11926 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11927 return MP_NO_ACTION;
11929 if (IS_EM_GATE(element))
11931 if (!player->key[EM_GATE_NR(element)])
11932 return MP_NO_ACTION;
11934 else if (IS_EM_GATE_GRAY(element))
11936 if (!player->key[EM_GATE_GRAY_NR(element)])
11937 return MP_NO_ACTION;
11939 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11941 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11942 return MP_NO_ACTION;
11944 else if (IS_EMC_GATE(element))
11946 if (!player->key[EMC_GATE_NR(element)])
11947 return MP_NO_ACTION;
11949 else if (IS_EMC_GATE_GRAY(element))
11951 if (!player->key[EMC_GATE_GRAY_NR(element)])
11952 return MP_NO_ACTION;
11954 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11956 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11957 return MP_NO_ACTION;
11959 else if (IS_SP_PORT(element))
11961 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11962 element == EL_SP_GRAVITY_PORT_RIGHT ||
11963 element == EL_SP_GRAVITY_PORT_UP ||
11964 element == EL_SP_GRAVITY_PORT_DOWN)
11965 #if USE_PLAYER_GRAVITY
11966 player->gravity = !player->gravity;
11968 game.gravity = !game.gravity;
11970 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11971 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11972 element == EL_SP_GRAVITY_ON_PORT_UP ||
11973 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11974 #if USE_PLAYER_GRAVITY
11975 player->gravity = TRUE;
11977 game.gravity = TRUE;
11979 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11980 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11981 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11982 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11983 #if USE_PLAYER_GRAVITY
11984 player->gravity = FALSE;
11986 game.gravity = FALSE;
11990 /* automatically move to the next field with double speed */
11991 player->programmed_action = move_direction;
11993 if (player->move_delay_reset_counter == 0)
11995 player->move_delay_reset_counter = 2; /* two double speed steps */
11997 DOUBLE_PLAYER_SPEED(player);
12000 PlayLevelSoundAction(x, y, ACTION_PASSING);
12002 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12006 if (mode != DF_SNAP)
12008 GfxElement[x][y] = GFX_ELEMENT(element);
12009 player->is_digging = TRUE;
12012 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12014 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12015 player->index_bit, dig_side);
12017 if (mode == DF_SNAP)
12019 #if USE_NEW_SNAP_DELAY
12020 if (level.block_snap_field)
12021 setFieldForSnapping(x, y, element, move_direction);
12023 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12025 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12028 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12029 player->index_bit, dig_side);
12032 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12036 if (is_player && mode != DF_SNAP)
12038 GfxElement[x][y] = element;
12039 player->is_collecting = TRUE;
12042 if (element == EL_SPEED_PILL)
12044 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12046 else if (element == EL_EXTRA_TIME && level.time > 0)
12048 TimeLeft += level.extra_time;
12049 DrawGameValue_Time(TimeLeft);
12051 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12053 player->shield_normal_time_left += level.shield_normal_time;
12054 if (element == EL_SHIELD_DEADLY)
12055 player->shield_deadly_time_left += level.shield_deadly_time;
12057 else if (element == EL_DYNAMITE ||
12058 element == EL_EM_DYNAMITE ||
12059 element == EL_SP_DISK_RED)
12061 if (player->inventory_size < MAX_INVENTORY_SIZE)
12062 player->inventory_element[player->inventory_size++] = element;
12065 DrawGameDoorValues();
12067 DrawGameValue_Dynamite(local_player->inventory_size);
12070 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12072 player->dynabomb_count++;
12073 player->dynabombs_left++;
12075 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12077 player->dynabomb_size++;
12079 else if (element == EL_DYNABOMB_INCREASE_POWER)
12081 player->dynabomb_xl = TRUE;
12083 else if (IS_KEY(element))
12085 player->key[KEY_NR(element)] = TRUE;
12088 DrawGameDoorValues();
12090 DrawGameValue_Keys(player->key);
12093 redraw_mask |= REDRAW_DOOR_1;
12095 else if (IS_ENVELOPE(element))
12097 player->show_envelope = element;
12099 else if (element == EL_EMC_LENSES)
12101 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12103 RedrawAllInvisibleElementsForLenses();
12105 else if (element == EL_EMC_MAGNIFIER)
12107 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12109 RedrawAllInvisibleElementsForMagnifier();
12111 else if (IS_DROPPABLE(element) ||
12112 IS_THROWABLE(element)) /* can be collected and dropped */
12116 if (collect_count == 0)
12117 player->inventory_infinite_element = element;
12119 for (i = 0; i < collect_count; i++)
12120 if (player->inventory_size < MAX_INVENTORY_SIZE)
12121 player->inventory_element[player->inventory_size++] = element;
12124 DrawGameDoorValues();
12126 DrawGameValue_Dynamite(local_player->inventory_size);
12129 else if (collect_count > 0)
12131 local_player->gems_still_needed -= collect_count;
12132 if (local_player->gems_still_needed < 0)
12133 local_player->gems_still_needed = 0;
12135 DrawGameValue_Emeralds(local_player->gems_still_needed);
12138 RaiseScoreElement(element);
12139 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12142 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12143 player->index_bit, dig_side);
12145 if (mode == DF_SNAP)
12147 #if USE_NEW_SNAP_DELAY
12148 if (level.block_snap_field)
12149 setFieldForSnapping(x, y, element, move_direction);
12151 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12153 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12156 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12157 player->index_bit, dig_side);
12160 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12162 if (mode == DF_SNAP && element != EL_BD_ROCK)
12163 return MP_NO_ACTION;
12165 if (CAN_FALL(element) && dy)
12166 return MP_NO_ACTION;
12168 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12169 !(element == EL_SPRING && level.use_spring_bug))
12170 return MP_NO_ACTION;
12172 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12173 ((move_direction & MV_VERTICAL &&
12174 ((element_info[element].move_pattern & MV_LEFT &&
12175 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12176 (element_info[element].move_pattern & MV_RIGHT &&
12177 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12178 (move_direction & MV_HORIZONTAL &&
12179 ((element_info[element].move_pattern & MV_UP &&
12180 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12181 (element_info[element].move_pattern & MV_DOWN &&
12182 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12183 return MP_NO_ACTION;
12185 /* do not push elements already moving away faster than player */
12186 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12187 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12188 return MP_NO_ACTION;
12190 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12192 if (player->push_delay_value == -1 || !player_was_pushing)
12193 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12195 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12197 if (player->push_delay_value == -1)
12198 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12200 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12202 if (!player->is_pushing)
12203 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12206 player->is_pushing = TRUE;
12207 player->is_active = TRUE;
12209 if (!(IN_LEV_FIELD(nextx, nexty) &&
12210 (IS_FREE(nextx, nexty) ||
12211 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12212 IS_SB_ELEMENT(element)))))
12213 return MP_NO_ACTION;
12215 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12216 return MP_NO_ACTION;
12218 if (player->push_delay == -1) /* new pushing; restart delay */
12219 player->push_delay = 0;
12221 if (player->push_delay < player->push_delay_value &&
12222 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12223 element != EL_SPRING && element != EL_BALLOON)
12225 /* make sure that there is no move delay before next try to push */
12226 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12227 player->move_delay = 0;
12229 return MP_NO_ACTION;
12232 if (IS_SB_ELEMENT(element))
12234 if (element == EL_SOKOBAN_FIELD_FULL)
12236 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12237 local_player->sokobanfields_still_needed++;
12240 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12242 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12243 local_player->sokobanfields_still_needed--;
12246 Feld[x][y] = EL_SOKOBAN_OBJECT;
12248 if (Back[x][y] == Back[nextx][nexty])
12249 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12250 else if (Back[x][y] != 0)
12251 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12254 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12257 if (local_player->sokobanfields_still_needed == 0 &&
12258 game.emulation == EMU_SOKOBAN)
12260 player->LevelSolved = player->GameOver = TRUE;
12261 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12265 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12267 InitMovingField(x, y, move_direction);
12268 GfxAction[x][y] = ACTION_PUSHING;
12270 if (mode == DF_SNAP)
12271 ContinueMoving(x, y);
12273 MovPos[x][y] = (dx != 0 ? dx : dy);
12275 Pushed[x][y] = TRUE;
12276 Pushed[nextx][nexty] = TRUE;
12278 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12279 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12281 player->push_delay_value = -1; /* get new value later */
12283 /* check for element change _after_ element has been pushed */
12284 if (game.use_change_when_pushing_bug)
12286 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12287 player->index_bit, dig_side);
12288 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12289 player->index_bit, dig_side);
12292 else if (IS_SWITCHABLE(element))
12294 if (PLAYER_SWITCHING(player, x, y))
12296 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12297 player->index_bit, dig_side);
12302 player->is_switching = TRUE;
12303 player->switch_x = x;
12304 player->switch_y = y;
12306 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12308 if (element == EL_ROBOT_WHEEL)
12310 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12314 DrawLevelField(x, y);
12316 else if (element == EL_SP_TERMINAL)
12321 SCAN_PLAYFIELD(xx, yy)
12323 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12326 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12328 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12329 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12332 else if (IS_BELT_SWITCH(element))
12334 ToggleBeltSwitch(x, y);
12336 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12337 element == EL_SWITCHGATE_SWITCH_DOWN)
12339 ToggleSwitchgateSwitch(x, y);
12341 else if (element == EL_LIGHT_SWITCH ||
12342 element == EL_LIGHT_SWITCH_ACTIVE)
12344 ToggleLightSwitch(x, y);
12346 else if (element == EL_TIMEGATE_SWITCH)
12348 ActivateTimegateSwitch(x, y);
12350 else if (element == EL_BALLOON_SWITCH_LEFT ||
12351 element == EL_BALLOON_SWITCH_RIGHT ||
12352 element == EL_BALLOON_SWITCH_UP ||
12353 element == EL_BALLOON_SWITCH_DOWN ||
12354 element == EL_BALLOON_SWITCH_NONE ||
12355 element == EL_BALLOON_SWITCH_ANY)
12357 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12358 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12359 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12360 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12361 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12364 else if (element == EL_LAMP)
12366 Feld[x][y] = EL_LAMP_ACTIVE;
12367 local_player->lights_still_needed--;
12369 ResetGfxAnimation(x, y);
12370 DrawLevelField(x, y);
12372 else if (element == EL_TIME_ORB_FULL)
12374 Feld[x][y] = EL_TIME_ORB_EMPTY;
12376 if (level.time > 0 || level.use_time_orb_bug)
12378 TimeLeft += level.time_orb_time;
12379 DrawGameValue_Time(TimeLeft);
12382 ResetGfxAnimation(x, y);
12383 DrawLevelField(x, y);
12385 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12386 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12390 game.ball_state = !game.ball_state;
12393 SCAN_PLAYFIELD(xx, yy)
12395 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12398 int e = Feld[xx][yy];
12400 if (game.ball_state)
12402 if (e == EL_EMC_MAGIC_BALL)
12403 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12404 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12405 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12409 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12410 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12411 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12412 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12417 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12418 player->index_bit, dig_side);
12420 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12421 player->index_bit, dig_side);
12423 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12424 player->index_bit, dig_side);
12430 if (!PLAYER_SWITCHING(player, x, y))
12432 player->is_switching = TRUE;
12433 player->switch_x = x;
12434 player->switch_y = y;
12436 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12437 player->index_bit, dig_side);
12438 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12439 player->index_bit, dig_side);
12441 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12442 player->index_bit, dig_side);
12443 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12444 player->index_bit, dig_side);
12447 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12448 player->index_bit, dig_side);
12449 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12450 player->index_bit, dig_side);
12452 return MP_NO_ACTION;
12455 player->push_delay = -1;
12457 if (is_player) /* function can also be called by EL_PENGUIN */
12459 if (Feld[x][y] != element) /* really digged/collected something */
12461 player->is_collecting = !player->is_digging;
12462 player->is_active = TRUE;
12469 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12471 int jx = player->jx, jy = player->jy;
12472 int x = jx + dx, y = jy + dy;
12473 int snap_direction = (dx == -1 ? MV_LEFT :
12474 dx == +1 ? MV_RIGHT :
12476 dy == +1 ? MV_DOWN : MV_NONE);
12477 boolean can_continue_snapping = (level.continuous_snapping &&
12478 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12480 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12483 if (!player->active || !IN_LEV_FIELD(x, y))
12491 if (player->MovPos == 0)
12492 player->is_pushing = FALSE;
12494 player->is_snapping = FALSE;
12496 if (player->MovPos == 0)
12498 player->is_moving = FALSE;
12499 player->is_digging = FALSE;
12500 player->is_collecting = FALSE;
12506 #if USE_NEW_CONTINUOUS_SNAPPING
12507 /* prevent snapping with already pressed snap key when not allowed */
12508 if (player->is_snapping && !can_continue_snapping)
12511 if (player->is_snapping)
12515 player->MovDir = snap_direction;
12517 if (player->MovPos == 0)
12519 player->is_moving = FALSE;
12520 player->is_digging = FALSE;
12521 player->is_collecting = FALSE;
12524 player->is_dropping = FALSE;
12525 player->is_dropping_pressed = FALSE;
12526 player->drop_pressed_delay = 0;
12528 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12531 player->is_snapping = TRUE;
12532 player->is_active = TRUE;
12534 if (player->MovPos == 0)
12536 player->is_moving = FALSE;
12537 player->is_digging = FALSE;
12538 player->is_collecting = FALSE;
12541 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12542 DrawLevelField(player->last_jx, player->last_jy);
12544 DrawLevelField(x, y);
12549 boolean DropElement(struct PlayerInfo *player)
12551 int old_element, new_element;
12552 int dropx = player->jx, dropy = player->jy;
12553 int drop_direction = player->MovDir;
12554 int drop_side = drop_direction;
12555 int drop_element = (player->inventory_size > 0 ?
12556 player->inventory_element[player->inventory_size - 1] :
12557 player->inventory_infinite_element != EL_UNDEFINED ?
12558 player->inventory_infinite_element :
12559 player->dynabombs_left > 0 ?
12560 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12563 player->is_dropping_pressed = TRUE;
12565 /* do not drop an element on top of another element; when holding drop key
12566 pressed without moving, dropped element must move away before the next
12567 element can be dropped (this is especially important if the next element
12568 is dynamite, which can be placed on background for historical reasons) */
12569 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12572 if (IS_THROWABLE(drop_element))
12574 dropx += GET_DX_FROM_DIR(drop_direction);
12575 dropy += GET_DY_FROM_DIR(drop_direction);
12577 if (!IN_LEV_FIELD(dropx, dropy))
12581 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12582 new_element = drop_element; /* default: no change when dropping */
12584 /* check if player is active, not moving and ready to drop */
12585 if (!player->active || player->MovPos || player->drop_delay > 0)
12588 /* check if player has anything that can be dropped */
12589 if (new_element == EL_UNDEFINED)
12592 /* check if drop key was pressed long enough for EM style dynamite */
12593 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12596 /* check if anything can be dropped at the current position */
12597 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12600 /* collected custom elements can only be dropped on empty fields */
12601 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12604 if (old_element != EL_EMPTY)
12605 Back[dropx][dropy] = old_element; /* store old element on this field */
12607 ResetGfxAnimation(dropx, dropy);
12608 ResetRandomAnimationValue(dropx, dropy);
12610 if (player->inventory_size > 0 ||
12611 player->inventory_infinite_element != EL_UNDEFINED)
12613 if (player->inventory_size > 0)
12615 player->inventory_size--;
12618 DrawGameDoorValues();
12620 DrawGameValue_Dynamite(local_player->inventory_size);
12623 if (new_element == EL_DYNAMITE)
12624 new_element = EL_DYNAMITE_ACTIVE;
12625 else if (new_element == EL_EM_DYNAMITE)
12626 new_element = EL_EM_DYNAMITE_ACTIVE;
12627 else if (new_element == EL_SP_DISK_RED)
12628 new_element = EL_SP_DISK_RED_ACTIVE;
12631 Feld[dropx][dropy] = new_element;
12633 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12634 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12635 el2img(Feld[dropx][dropy]), 0);
12637 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12639 /* needed if previous element just changed to "empty" in the last frame */
12640 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12642 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12643 player->index_bit, drop_side);
12644 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12646 player->index_bit, drop_side);
12648 TestIfElementTouchesCustomElement(dropx, dropy);
12650 else /* player is dropping a dyna bomb */
12652 player->dynabombs_left--;
12654 Feld[dropx][dropy] = new_element;
12656 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12657 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12658 el2img(Feld[dropx][dropy]), 0);
12660 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12663 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12664 InitField_WithBug1(dropx, dropy, FALSE);
12666 new_element = Feld[dropx][dropy]; /* element might have changed */
12668 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12669 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12671 int move_direction, nextx, nexty;
12673 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12674 MovDir[dropx][dropy] = drop_direction;
12676 move_direction = MovDir[dropx][dropy];
12677 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12678 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12680 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12681 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12684 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12685 player->is_dropping = TRUE;
12687 player->drop_pressed_delay = 0;
12688 player->is_dropping_pressed = FALSE;
12690 player->drop_x = dropx;
12691 player->drop_y = dropy;
12696 /* ------------------------------------------------------------------------- */
12697 /* game sound playing functions */
12698 /* ------------------------------------------------------------------------- */
12700 static int *loop_sound_frame = NULL;
12701 static int *loop_sound_volume = NULL;
12703 void InitPlayLevelSound()
12705 int num_sounds = getSoundListSize();
12707 checked_free(loop_sound_frame);
12708 checked_free(loop_sound_volume);
12710 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12711 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12714 static void PlayLevelSound(int x, int y, int nr)
12716 int sx = SCREENX(x), sy = SCREENY(y);
12717 int volume, stereo_position;
12718 int max_distance = 8;
12719 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12721 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12722 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12725 if (!IN_LEV_FIELD(x, y) ||
12726 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12727 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12730 volume = SOUND_MAX_VOLUME;
12732 if (!IN_SCR_FIELD(sx, sy))
12734 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12735 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12737 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12740 stereo_position = (SOUND_MAX_LEFT +
12741 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12742 (SCR_FIELDX + 2 * max_distance));
12744 if (IS_LOOP_SOUND(nr))
12746 /* This assures that quieter loop sounds do not overwrite louder ones,
12747 while restarting sound volume comparison with each new game frame. */
12749 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12752 loop_sound_volume[nr] = volume;
12753 loop_sound_frame[nr] = FrameCounter;
12756 PlaySoundExt(nr, volume, stereo_position, type);
12759 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12761 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12762 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12763 y < LEVELY(BY1) ? LEVELY(BY1) :
12764 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12768 static void PlayLevelSoundAction(int x, int y, int action)
12770 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12773 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12775 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12777 if (sound_effect != SND_UNDEFINED)
12778 PlayLevelSound(x, y, sound_effect);
12781 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12784 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12786 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12787 PlayLevelSound(x, y, sound_effect);
12790 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12792 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12794 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12795 PlayLevelSound(x, y, sound_effect);
12798 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12800 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12802 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12803 StopSound(sound_effect);
12806 static void PlayLevelMusic()
12808 if (levelset.music[level_nr] != MUS_UNDEFINED)
12809 PlayMusic(levelset.music[level_nr]); /* from config file */
12811 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12814 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12816 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12821 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12825 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12829 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12833 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12837 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12841 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12845 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12848 case SAMPLE_android_clone:
12849 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12852 case SAMPLE_android_move:
12853 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12856 case SAMPLE_spring:
12857 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12861 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12865 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12868 case SAMPLE_eater_eat:
12869 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12873 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12876 case SAMPLE_collect:
12877 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12880 case SAMPLE_diamond:
12881 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12884 case SAMPLE_squash:
12885 /* !!! CHECK THIS !!! */
12887 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12889 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12893 case SAMPLE_wonderfall:
12894 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12898 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12902 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12906 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12910 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12914 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12918 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12921 case SAMPLE_wonder:
12922 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12926 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12929 case SAMPLE_exit_open:
12930 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12933 case SAMPLE_exit_leave:
12934 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12937 case SAMPLE_dynamite:
12938 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12942 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12946 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12950 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12954 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12958 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12962 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12966 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12971 void RaiseScore(int value)
12973 local_player->score += value;
12975 DrawGameValue_Score(local_player->score);
12978 void RaiseScoreElement(int element)
12983 case EL_BD_DIAMOND:
12984 case EL_EMERALD_YELLOW:
12985 case EL_EMERALD_RED:
12986 case EL_EMERALD_PURPLE:
12987 case EL_SP_INFOTRON:
12988 RaiseScore(level.score[SC_EMERALD]);
12991 RaiseScore(level.score[SC_DIAMOND]);
12994 RaiseScore(level.score[SC_CRYSTAL]);
12997 RaiseScore(level.score[SC_PEARL]);
13000 case EL_BD_BUTTERFLY:
13001 case EL_SP_ELECTRON:
13002 RaiseScore(level.score[SC_BUG]);
13005 case EL_BD_FIREFLY:
13006 case EL_SP_SNIKSNAK:
13007 RaiseScore(level.score[SC_SPACESHIP]);
13010 case EL_DARK_YAMYAM:
13011 RaiseScore(level.score[SC_YAMYAM]);
13014 RaiseScore(level.score[SC_ROBOT]);
13017 RaiseScore(level.score[SC_PACMAN]);
13020 RaiseScore(level.score[SC_NUT]);
13023 case EL_EM_DYNAMITE:
13024 case EL_SP_DISK_RED:
13025 case EL_DYNABOMB_INCREASE_NUMBER:
13026 case EL_DYNABOMB_INCREASE_SIZE:
13027 case EL_DYNABOMB_INCREASE_POWER:
13028 RaiseScore(level.score[SC_DYNAMITE]);
13030 case EL_SHIELD_NORMAL:
13031 case EL_SHIELD_DEADLY:
13032 RaiseScore(level.score[SC_SHIELD]);
13034 case EL_EXTRA_TIME:
13035 RaiseScore(level.extra_time_score);
13049 RaiseScore(level.score[SC_KEY]);
13052 RaiseScore(element_info[element].collect_score);
13057 void RequestQuitGame(boolean ask_if_really_quit)
13059 if (AllPlayersGone ||
13060 !ask_if_really_quit ||
13061 level_editor_test_game ||
13062 Request("Do you really want to quit the game ?",
13063 REQ_ASK | REQ_STAY_CLOSED))
13065 #if defined(NETWORK_AVALIABLE)
13066 if (options.network)
13067 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13071 game_status = GAME_MODE_MAIN;
13077 if (tape.playing && tape.deactivate_display)
13078 TapeDeactivateDisplayOff(TRUE);
13080 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13082 if (tape.playing && tape.deactivate_display)
13083 TapeDeactivateDisplayOn();
13088 /* ---------- new game button stuff ---------------------------------------- */
13090 /* graphic position values for game buttons */
13091 #define GAME_BUTTON_XSIZE 30
13092 #define GAME_BUTTON_YSIZE 30
13093 #define GAME_BUTTON_XPOS 5
13094 #define GAME_BUTTON_YPOS 215
13095 #define SOUND_BUTTON_XPOS 5
13096 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13098 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13099 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13100 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13101 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13102 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13103 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13110 } gamebutton_info[NUM_GAME_BUTTONS] =
13113 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13118 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13119 GAME_CTRL_ID_PAUSE,
13123 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13128 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13129 SOUND_CTRL_ID_MUSIC,
13130 "background music on/off"
13133 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13134 SOUND_CTRL_ID_LOOPS,
13135 "sound loops on/off"
13138 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13139 SOUND_CTRL_ID_SIMPLE,
13140 "normal sounds on/off"
13144 void CreateGameButtons()
13148 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13150 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13151 struct GadgetInfo *gi;
13154 unsigned long event_mask;
13155 int gd_xoffset, gd_yoffset;
13156 int gd_x1, gd_x2, gd_y1, gd_y2;
13159 gd_xoffset = gamebutton_info[i].x;
13160 gd_yoffset = gamebutton_info[i].y;
13161 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13162 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13164 if (id == GAME_CTRL_ID_STOP ||
13165 id == GAME_CTRL_ID_PAUSE ||
13166 id == GAME_CTRL_ID_PLAY)
13168 button_type = GD_TYPE_NORMAL_BUTTON;
13170 event_mask = GD_EVENT_RELEASED;
13171 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13172 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13176 button_type = GD_TYPE_CHECK_BUTTON;
13178 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13179 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13180 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13181 event_mask = GD_EVENT_PRESSED;
13182 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13183 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13186 gi = CreateGadget(GDI_CUSTOM_ID, id,
13187 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13188 GDI_X, DX + gd_xoffset,
13189 GDI_Y, DY + gd_yoffset,
13190 GDI_WIDTH, GAME_BUTTON_XSIZE,
13191 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13192 GDI_TYPE, button_type,
13193 GDI_STATE, GD_BUTTON_UNPRESSED,
13194 GDI_CHECKED, checked,
13195 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13196 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13197 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13198 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13199 GDI_EVENT_MASK, event_mask,
13200 GDI_CALLBACK_ACTION, HandleGameButtons,
13204 Error(ERR_EXIT, "cannot create gadget");
13206 game_gadget[id] = gi;
13210 void FreeGameButtons()
13214 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13215 FreeGadget(game_gadget[i]);
13218 static void MapGameButtons()
13222 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13223 MapGadget(game_gadget[i]);
13226 void UnmapGameButtons()
13230 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13231 UnmapGadget(game_gadget[i]);
13234 static void HandleGameButtons(struct GadgetInfo *gi)
13236 int id = gi->custom_id;
13238 if (game_status != GAME_MODE_PLAYING)
13243 case GAME_CTRL_ID_STOP:
13247 RequestQuitGame(TRUE);
13250 case GAME_CTRL_ID_PAUSE:
13251 if (options.network)
13253 #if defined(NETWORK_AVALIABLE)
13255 SendToServer_ContinuePlaying();
13257 SendToServer_PausePlaying();
13261 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13264 case GAME_CTRL_ID_PLAY:
13267 #if defined(NETWORK_AVALIABLE)
13268 if (options.network)
13269 SendToServer_ContinuePlaying();
13273 tape.pausing = FALSE;
13274 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13279 case SOUND_CTRL_ID_MUSIC:
13280 if (setup.sound_music)
13282 setup.sound_music = FALSE;
13285 else if (audio.music_available)
13287 setup.sound = setup.sound_music = TRUE;
13289 SetAudioMode(setup.sound);
13295 case SOUND_CTRL_ID_LOOPS:
13296 if (setup.sound_loops)
13297 setup.sound_loops = FALSE;
13298 else if (audio.loops_available)
13300 setup.sound = setup.sound_loops = TRUE;
13301 SetAudioMode(setup.sound);
13305 case SOUND_CTRL_ID_SIMPLE:
13306 if (setup.sound_simple)
13307 setup.sound_simple = FALSE;
13308 else if (audio.sound_available)
13310 setup.sound = setup.sound_simple = TRUE;
13311 SetAudioMode(setup.sound);