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 ||
7814 element == EL_BD_EXPANDABLE_WALL)
7818 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7819 Store[ax-1][ay] = element;
7820 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7821 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7822 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7823 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7829 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7830 Store[ax+1][ay] = element;
7831 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7832 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7833 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7834 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7839 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7840 DrawLevelField(ax, ay);
7842 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7844 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7845 unten_massiv = TRUE;
7846 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7847 links_massiv = TRUE;
7848 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7849 rechts_massiv = TRUE;
7851 if (((oben_massiv && unten_massiv) ||
7852 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7853 element == EL_EXPANDABLE_WALL) &&
7854 ((links_massiv && rechts_massiv) ||
7855 element == EL_EXPANDABLE_WALL_VERTICAL))
7856 Feld[ax][ay] = EL_WALL;
7859 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7862 void CheckForDragon(int x, int y)
7865 boolean dragon_found = FALSE;
7866 static int xy[4][2] =
7874 for (i = 0; i < NUM_DIRECTIONS; i++)
7876 for (j = 0; j < 4; j++)
7878 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7880 if (IN_LEV_FIELD(xx, yy) &&
7881 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7883 if (Feld[xx][yy] == EL_DRAGON)
7884 dragon_found = TRUE;
7893 for (i = 0; i < NUM_DIRECTIONS; i++)
7895 for (j = 0; j < 3; j++)
7897 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7899 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7901 Feld[xx][yy] = EL_EMPTY;
7902 DrawLevelField(xx, yy);
7911 static void InitBuggyBase(int x, int y)
7913 int element = Feld[x][y];
7914 int activating_delay = FRAMES_PER_SECOND / 4;
7917 (element == EL_SP_BUGGY_BASE ?
7918 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7919 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7921 element == EL_SP_BUGGY_BASE_ACTIVE ?
7922 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7925 static void WarnBuggyBase(int x, int y)
7928 static int xy[4][2] =
7936 for (i = 0; i < NUM_DIRECTIONS; i++)
7938 int xx = x + xy[i][0];
7939 int yy = y + xy[i][1];
7941 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7943 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7950 static void InitTrap(int x, int y)
7952 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7955 static void ActivateTrap(int x, int y)
7957 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7960 static void ChangeActiveTrap(int x, int y)
7962 int graphic = IMG_TRAP_ACTIVE;
7964 /* if new animation frame was drawn, correct crumbled sand border */
7965 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7966 DrawLevelFieldCrumbledSand(x, y);
7969 static int getSpecialActionElement(int element, int number, int base_element)
7971 return (element != EL_EMPTY ? element :
7972 number != -1 ? base_element + number - 1 :
7976 static int getModifiedActionNumber(int value_old, int operator, int operand,
7977 int value_min, int value_max)
7979 int value_new = (operator == CA_MODE_SET ? operand :
7980 operator == CA_MODE_ADD ? value_old + operand :
7981 operator == CA_MODE_SUBTRACT ? value_old - operand :
7982 operator == CA_MODE_MULTIPLY ? value_old * operand :
7983 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7984 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7987 return (value_new < value_min ? value_min :
7988 value_new > value_max ? value_max :
7992 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7994 struct ElementInfo *ei = &element_info[element];
7995 struct ElementChangeInfo *change = &ei->change_page[page];
7996 int target_element = change->target_element;
7997 int action_type = change->action_type;
7998 int action_mode = change->action_mode;
7999 int action_arg = change->action_arg;
8002 if (!change->has_action)
8005 /* ---------- determine action paramater values -------------------------- */
8007 int level_time_value =
8008 (level.time > 0 ? TimeLeft :
8011 int action_arg_element =
8012 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8013 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8014 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8017 int action_arg_direction =
8018 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8019 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8020 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8021 change->actual_trigger_side :
8022 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8023 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8026 int action_arg_number_min =
8027 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8030 int action_arg_number_max =
8031 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8032 action_type == CA_SET_LEVEL_GEMS ? 999 :
8033 action_type == CA_SET_LEVEL_TIME ? 9999 :
8034 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8035 action_type == CA_SET_CE_VALUE ? 9999 :
8036 action_type == CA_SET_CE_SCORE ? 9999 :
8039 int action_arg_number_reset =
8040 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8041 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8042 action_type == CA_SET_LEVEL_TIME ? level.time :
8043 action_type == CA_SET_LEVEL_SCORE ? 0 :
8045 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8047 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8049 action_type == CA_SET_CE_SCORE ? 0 :
8052 int action_arg_number =
8053 (action_arg <= CA_ARG_MAX ? action_arg :
8054 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8055 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8056 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8057 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8058 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8059 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8060 #if USE_NEW_CUSTOM_VALUE
8061 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8063 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8065 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8066 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8067 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8068 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8069 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8070 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8071 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8072 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8073 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8074 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8075 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8078 int action_arg_number_old =
8079 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8080 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8081 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8082 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8083 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8086 int action_arg_number_new =
8087 getModifiedActionNumber(action_arg_number_old,
8088 action_mode, action_arg_number,
8089 action_arg_number_min, action_arg_number_max);
8091 int trigger_player_bits =
8092 (change->actual_trigger_player >= EL_PLAYER_1 &&
8093 change->actual_trigger_player <= EL_PLAYER_4 ?
8094 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8097 int action_arg_player_bits =
8098 (action_arg >= CA_ARG_PLAYER_1 &&
8099 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8100 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8103 /* ---------- execute action -------------------------------------------- */
8112 /* ---------- level actions ------------------------------------------- */
8114 case CA_RESTART_LEVEL:
8116 game.restart_level = TRUE;
8121 case CA_SHOW_ENVELOPE:
8123 int element = getSpecialActionElement(action_arg_element,
8124 action_arg_number, EL_ENVELOPE_1);
8126 if (IS_ENVELOPE(element))
8127 local_player->show_envelope = element;
8132 case CA_SET_LEVEL_TIME:
8134 if (level.time > 0) /* only modify limited time value */
8136 TimeLeft = action_arg_number_new;
8138 DrawGameValue_Time(TimeLeft);
8140 if (!TimeLeft && setup.time_limit)
8141 for (i = 0; i < MAX_PLAYERS; i++)
8142 KillPlayer(&stored_player[i]);
8148 case CA_SET_LEVEL_SCORE:
8150 local_player->score = action_arg_number_new;
8152 DrawGameValue_Score(local_player->score);
8157 case CA_SET_LEVEL_GEMS:
8159 local_player->gems_still_needed = action_arg_number_new;
8161 DrawGameValue_Emeralds(local_player->gems_still_needed);
8166 #if !USE_PLAYER_GRAVITY
8167 case CA_SET_LEVEL_GRAVITY:
8169 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8170 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8171 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8177 case CA_SET_LEVEL_WIND:
8179 game.wind_direction = action_arg_direction;
8184 /* ---------- player actions ------------------------------------------ */
8186 case CA_MOVE_PLAYER:
8188 /* automatically move to the next field in specified direction */
8189 for (i = 0; i < MAX_PLAYERS; i++)
8190 if (trigger_player_bits & (1 << i))
8191 stored_player[i].programmed_action = action_arg_direction;
8196 case CA_EXIT_PLAYER:
8198 for (i = 0; i < MAX_PLAYERS; i++)
8199 if (action_arg_player_bits & (1 << i))
8200 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8205 case CA_KILL_PLAYER:
8207 for (i = 0; i < MAX_PLAYERS; i++)
8208 if (action_arg_player_bits & (1 << i))
8209 KillPlayer(&stored_player[i]);
8214 case CA_SET_PLAYER_KEYS:
8216 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8217 int element = getSpecialActionElement(action_arg_element,
8218 action_arg_number, EL_KEY_1);
8220 if (IS_KEY(element))
8222 for (i = 0; i < MAX_PLAYERS; i++)
8224 if (trigger_player_bits & (1 << i))
8226 stored_player[i].key[KEY_NR(element)] = key_state;
8229 DrawGameDoorValues();
8231 DrawGameValue_Keys(stored_player[i].key);
8234 redraw_mask |= REDRAW_DOOR_1;
8242 case CA_SET_PLAYER_SPEED:
8244 for (i = 0; i < MAX_PLAYERS; i++)
8246 if (trigger_player_bits & (1 << i))
8248 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8250 if (action_arg == CA_ARG_SPEED_FASTER &&
8251 stored_player[i].cannot_move)
8253 action_arg_number = STEPSIZE_VERY_SLOW;
8255 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8256 action_arg == CA_ARG_SPEED_FASTER)
8258 action_arg_number = 2;
8259 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8262 else if (action_arg == CA_ARG_NUMBER_RESET)
8264 action_arg_number = level.initial_player_stepsize[i];
8268 getModifiedActionNumber(move_stepsize,
8271 action_arg_number_min,
8272 action_arg_number_max);
8275 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8277 /* make sure that value is power of 2 */
8278 move_stepsize = (1 << log_2(move_stepsize));
8280 /* do no immediately change -- the player might just be moving */
8281 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8283 stored_player[i].cannot_move =
8284 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8292 case CA_SET_PLAYER_SHIELD:
8294 for (i = 0; i < MAX_PLAYERS; i++)
8296 if (trigger_player_bits & (1 << i))
8298 if (action_arg == CA_ARG_SHIELD_OFF)
8300 stored_player[i].shield_normal_time_left = 0;
8301 stored_player[i].shield_deadly_time_left = 0;
8303 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8305 stored_player[i].shield_normal_time_left = 999999;
8307 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8309 stored_player[i].shield_normal_time_left = 999999;
8310 stored_player[i].shield_deadly_time_left = 999999;
8318 #if USE_PLAYER_GRAVITY
8319 case CA_SET_PLAYER_GRAVITY:
8321 for (i = 0; i < MAX_PLAYERS; i++)
8323 if (trigger_player_bits & (1 << i))
8325 stored_player[i].gravity =
8326 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8327 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8328 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8329 stored_player[i].gravity);
8337 case CA_SET_PLAYER_ARTWORK:
8339 for (i = 0; i < MAX_PLAYERS; i++)
8341 if (trigger_player_bits & (1 << i))
8343 int artwork_element = action_arg_element;
8345 if (action_arg == CA_ARG_ELEMENT_RESET)
8347 (level.use_artwork_element[i] ? level.artwork_element[i] :
8348 stored_player[i].element_nr);
8350 stored_player[i].artwork_element = artwork_element;
8352 SetPlayerWaiting(&stored_player[i], FALSE);
8354 /* set number of special actions for bored and sleeping animation */
8355 stored_player[i].num_special_action_bored =
8356 get_num_special_action(artwork_element,
8357 ACTION_BORING_1, ACTION_BORING_LAST);
8358 stored_player[i].num_special_action_sleeping =
8359 get_num_special_action(artwork_element,
8360 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8367 /* ---------- CE actions ---------------------------------------------- */
8369 case CA_SET_CE_VALUE:
8371 #if USE_NEW_CUSTOM_VALUE
8372 int last_ce_value = CustomValue[x][y];
8374 CustomValue[x][y] = action_arg_number_new;
8377 printf("::: CE value == %d\n", CustomValue[x][y]);
8380 if (CustomValue[x][y] != last_ce_value)
8382 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8383 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8385 if (CustomValue[x][y] == 0)
8388 printf("::: CE_VALUE_GETS_ZERO\n");
8391 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8392 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8395 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8405 case CA_SET_CE_SCORE:
8407 #if USE_NEW_CUSTOM_VALUE
8408 int last_ce_score = ei->collect_score;
8410 ei->collect_score = action_arg_number_new;
8413 printf("::: CE score == %d\n", ei->collect_score);
8416 if (ei->collect_score != last_ce_score)
8418 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8419 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8421 if (ei->collect_score == 0)
8424 printf("::: CE_SCORE_GETS_ZERO\n");
8427 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8428 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8431 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8441 /* ---------- engine actions ------------------------------------------ */
8443 case CA_SET_ENGINE_SCAN_MODE:
8445 InitPlayfieldScanMode(action_arg);
8455 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8457 int old_element = Feld[x][y];
8458 int new_element = get_element_from_group_element(element);
8459 int previous_move_direction = MovDir[x][y];
8460 #if USE_NEW_CUSTOM_VALUE
8461 int last_ce_value = CustomValue[x][y];
8463 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8464 boolean add_player_onto_element = (new_element_is_player &&
8465 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8466 IS_WALKABLE(old_element));
8469 /* check if element under the player changes from accessible to unaccessible
8470 (needed for special case of dropping element which then changes) */
8471 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8472 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8480 if (!add_player_onto_element)
8482 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8483 RemoveMovingField(x, y);
8487 Feld[x][y] = new_element;
8489 #if !USE_GFX_RESET_GFX_ANIMATION
8490 ResetGfxAnimation(x, y);
8491 ResetRandomAnimationValue(x, y);
8494 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8495 MovDir[x][y] = previous_move_direction;
8497 #if USE_NEW_CUSTOM_VALUE
8498 if (element_info[new_element].use_last_ce_value)
8499 CustomValue[x][y] = last_ce_value;
8502 InitField_WithBug1(x, y, FALSE);
8504 new_element = Feld[x][y]; /* element may have changed */
8506 #if USE_GFX_RESET_GFX_ANIMATION
8507 ResetGfxAnimation(x, y);
8508 ResetRandomAnimationValue(x, y);
8511 DrawLevelField(x, y);
8513 if (GFX_CRUMBLED(new_element))
8514 DrawLevelFieldCrumbledSandNeighbours(x, y);
8518 /* check if element under the player changes from accessible to unaccessible
8519 (needed for special case of dropping element which then changes) */
8520 /* (must be checked after creating new element for walkable group elements) */
8521 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8522 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8530 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8531 if (new_element_is_player)
8532 RelocatePlayer(x, y, new_element);
8535 ChangeCount[x][y]++; /* count number of changes in the same frame */
8537 TestIfBadThingTouchesPlayer(x, y);
8538 TestIfPlayerTouchesCustomElement(x, y);
8539 TestIfElementTouchesCustomElement(x, y);
8542 static void CreateField(int x, int y, int element)
8544 CreateFieldExt(x, y, element, FALSE);
8547 static void CreateElementFromChange(int x, int y, int element)
8549 element = GET_VALID_RUNTIME_ELEMENT(element);
8551 #if USE_STOP_CHANGED_ELEMENTS
8552 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8554 int old_element = Feld[x][y];
8556 /* prevent changed element from moving in same engine frame
8557 unless both old and new element can either fall or move */
8558 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8559 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8564 CreateFieldExt(x, y, element, TRUE);
8567 static boolean ChangeElement(int x, int y, int element, int page)
8569 struct ElementInfo *ei = &element_info[element];
8570 struct ElementChangeInfo *change = &ei->change_page[page];
8571 int ce_value = CustomValue[x][y];
8572 int ce_score = ei->collect_score;
8574 int old_element = Feld[x][y];
8576 /* always use default change event to prevent running into a loop */
8577 if (ChangeEvent[x][y] == -1)
8578 ChangeEvent[x][y] = CE_DELAY;
8580 if (ChangeEvent[x][y] == CE_DELAY)
8582 /* reset actual trigger element, trigger player and action element */
8583 change->actual_trigger_element = EL_EMPTY;
8584 change->actual_trigger_player = EL_PLAYER_1;
8585 change->actual_trigger_side = CH_SIDE_NONE;
8586 change->actual_trigger_ce_value = 0;
8587 change->actual_trigger_ce_score = 0;
8590 /* do not change elements more than a specified maximum number of changes */
8591 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8594 ChangeCount[x][y]++; /* count number of changes in the same frame */
8596 if (change->explode)
8603 if (change->use_target_content)
8605 boolean complete_replace = TRUE;
8606 boolean can_replace[3][3];
8609 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8612 boolean is_walkable;
8613 boolean is_diggable;
8614 boolean is_collectible;
8615 boolean is_removable;
8616 boolean is_destructible;
8617 int ex = x + xx - 1;
8618 int ey = y + yy - 1;
8619 int content_element = change->target_content.e[xx][yy];
8622 can_replace[xx][yy] = TRUE;
8624 if (ex == x && ey == y) /* do not check changing element itself */
8627 if (content_element == EL_EMPTY_SPACE)
8629 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8634 if (!IN_LEV_FIELD(ex, ey))
8636 can_replace[xx][yy] = FALSE;
8637 complete_replace = FALSE;
8644 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8645 e = MovingOrBlocked2Element(ex, ey);
8647 is_empty = (IS_FREE(ex, ey) ||
8648 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8650 is_walkable = (is_empty || IS_WALKABLE(e));
8651 is_diggable = (is_empty || IS_DIGGABLE(e));
8652 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8653 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8654 is_removable = (is_diggable || is_collectible);
8656 can_replace[xx][yy] =
8657 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8658 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8659 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8660 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8661 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8662 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8663 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8665 if (!can_replace[xx][yy])
8666 complete_replace = FALSE;
8669 if (!change->only_if_complete || complete_replace)
8671 boolean something_has_changed = FALSE;
8673 if (change->only_if_complete && change->use_random_replace &&
8674 RND(100) < change->random_percentage)
8677 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8679 int ex = x + xx - 1;
8680 int ey = y + yy - 1;
8681 int content_element;
8683 if (can_replace[xx][yy] && (!change->use_random_replace ||
8684 RND(100) < change->random_percentage))
8686 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8687 RemoveMovingField(ex, ey);
8689 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8691 content_element = change->target_content.e[xx][yy];
8692 target_element = GET_TARGET_ELEMENT(content_element, change,
8693 ce_value, ce_score);
8695 CreateElementFromChange(ex, ey, target_element);
8697 something_has_changed = TRUE;
8699 /* for symmetry reasons, freeze newly created border elements */
8700 if (ex != x || ey != y)
8701 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8705 if (something_has_changed)
8707 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8708 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8714 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8715 ce_value, ce_score);
8717 if (element == EL_DIAGONAL_GROWING ||
8718 element == EL_DIAGONAL_SHRINKING)
8720 target_element = Store[x][y];
8722 Store[x][y] = EL_EMPTY;
8725 CreateElementFromChange(x, y, target_element);
8727 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8728 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8731 /* this uses direct change before indirect change */
8732 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8737 #if USE_NEW_DELAYED_ACTION
8739 static void HandleElementChange(int x, int y, int page)
8741 int element = MovingOrBlocked2Element(x, y);
8742 struct ElementInfo *ei = &element_info[element];
8743 struct ElementChangeInfo *change = &ei->change_page[page];
8746 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8747 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8750 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8751 x, y, element, element_info[element].token_name);
8752 printf("HandleElementChange(): This should never happen!\n");
8757 /* this can happen with classic bombs on walkable, changing elements */
8758 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8761 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8762 ChangeDelay[x][y] = 0;
8768 if (ChangeDelay[x][y] == 0) /* initialize element change */
8770 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8772 if (change->can_change)
8774 ResetGfxAnimation(x, y);
8775 ResetRandomAnimationValue(x, y);
8777 if (change->pre_change_function)
8778 change->pre_change_function(x, y);
8782 ChangeDelay[x][y]--;
8784 if (ChangeDelay[x][y] != 0) /* continue element change */
8786 if (change->can_change)
8788 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8790 if (IS_ANIMATED(graphic))
8791 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8793 if (change->change_function)
8794 change->change_function(x, y);
8797 else /* finish element change */
8799 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8801 page = ChangePage[x][y];
8802 ChangePage[x][y] = -1;
8804 change = &ei->change_page[page];
8807 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8809 ChangeDelay[x][y] = 1; /* try change after next move step */
8810 ChangePage[x][y] = page; /* remember page to use for change */
8815 if (change->can_change)
8817 if (ChangeElement(x, y, element, page))
8819 if (change->post_change_function)
8820 change->post_change_function(x, y);
8824 if (change->has_action)
8825 ExecuteCustomElementAction(x, y, element, page);
8831 static void HandleElementChange(int x, int y, int page)
8833 int element = MovingOrBlocked2Element(x, y);
8834 struct ElementInfo *ei = &element_info[element];
8835 struct ElementChangeInfo *change = &ei->change_page[page];
8838 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8841 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8842 x, y, element, element_info[element].token_name);
8843 printf("HandleElementChange(): This should never happen!\n");
8848 /* this can happen with classic bombs on walkable, changing elements */
8849 if (!CAN_CHANGE(element))
8852 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8853 ChangeDelay[x][y] = 0;
8859 if (ChangeDelay[x][y] == 0) /* initialize element change */
8861 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8863 ResetGfxAnimation(x, y);
8864 ResetRandomAnimationValue(x, y);
8866 if (change->pre_change_function)
8867 change->pre_change_function(x, y);
8870 ChangeDelay[x][y]--;
8872 if (ChangeDelay[x][y] != 0) /* continue element change */
8874 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8876 if (IS_ANIMATED(graphic))
8877 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8879 if (change->change_function)
8880 change->change_function(x, y);
8882 else /* finish element change */
8884 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8886 page = ChangePage[x][y];
8887 ChangePage[x][y] = -1;
8889 change = &ei->change_page[page];
8892 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8894 ChangeDelay[x][y] = 1; /* try change after next move step */
8895 ChangePage[x][y] = page; /* remember page to use for change */
8900 if (ChangeElement(x, y, element, page))
8902 if (change->post_change_function)
8903 change->post_change_function(x, y);
8910 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8911 int trigger_element,
8917 boolean change_done_any = FALSE;
8918 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8921 if (!(trigger_events[trigger_element][trigger_event]))
8924 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8926 int element = EL_CUSTOM_START + i;
8927 boolean change_done = FALSE;
8930 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8931 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8934 for (p = 0; p < element_info[element].num_change_pages; p++)
8936 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8938 if (change->can_change_or_has_action &&
8939 change->has_event[trigger_event] &&
8940 change->trigger_side & trigger_side &&
8941 change->trigger_player & trigger_player &&
8942 change->trigger_page & trigger_page_bits &&
8943 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8945 change->actual_trigger_element = trigger_element;
8946 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8947 change->actual_trigger_side = trigger_side;
8948 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8949 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8951 if ((change->can_change && !change_done) || change->has_action)
8956 SCAN_PLAYFIELD(x, y)
8958 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8961 if (Feld[x][y] == element)
8963 if (change->can_change && !change_done)
8965 ChangeDelay[x][y] = 1;
8966 ChangeEvent[x][y] = trigger_event;
8968 HandleElementChange(x, y, p);
8970 #if USE_NEW_DELAYED_ACTION
8971 else if (change->has_action)
8973 ExecuteCustomElementAction(x, y, element, p);
8974 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8977 if (change->has_action)
8979 ExecuteCustomElementAction(x, y, element, p);
8980 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8986 if (change->can_change)
8989 change_done_any = TRUE;
8996 return change_done_any;
8999 static boolean CheckElementChangeExt(int x, int y,
9001 int trigger_element,
9006 boolean change_done = FALSE;
9009 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9010 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9013 if (Feld[x][y] == EL_BLOCKED)
9015 Blocked2Moving(x, y, &x, &y);
9016 element = Feld[x][y];
9020 /* check if element has already changed */
9021 if (Feld[x][y] != element)
9024 /* check if element has already changed or is about to change after moving */
9025 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9026 Feld[x][y] != element) ||
9028 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9029 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9030 ChangePage[x][y] != -1)))
9034 for (p = 0; p < element_info[element].num_change_pages; p++)
9036 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9038 boolean check_trigger_element =
9039 (trigger_event == CE_TOUCHING_X ||
9040 trigger_event == CE_HITTING_X ||
9041 trigger_event == CE_HIT_BY_X);
9043 if (change->can_change_or_has_action &&
9044 change->has_event[trigger_event] &&
9045 change->trigger_side & trigger_side &&
9046 change->trigger_player & trigger_player &&
9047 (!check_trigger_element ||
9048 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9050 change->actual_trigger_element = trigger_element;
9051 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9052 change->actual_trigger_side = trigger_side;
9053 change->actual_trigger_ce_value = CustomValue[x][y];
9054 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9056 /* special case: trigger element not at (x,y) position for some events */
9057 if (check_trigger_element)
9069 { 0, 0 }, { 0, 0 }, { 0, 0 },
9073 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9074 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9076 change->actual_trigger_ce_value = CustomValue[xx][yy];
9077 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9080 if (change->can_change && !change_done)
9082 ChangeDelay[x][y] = 1;
9083 ChangeEvent[x][y] = trigger_event;
9085 HandleElementChange(x, y, p);
9089 #if USE_NEW_DELAYED_ACTION
9090 else if (change->has_action)
9092 ExecuteCustomElementAction(x, y, element, p);
9093 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9096 if (change->has_action)
9098 ExecuteCustomElementAction(x, y, element, p);
9099 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9108 static void PlayPlayerSound(struct PlayerInfo *player)
9110 int jx = player->jx, jy = player->jy;
9111 int sound_element = player->artwork_element;
9112 int last_action = player->last_action_waiting;
9113 int action = player->action_waiting;
9115 if (player->is_waiting)
9117 if (action != last_action)
9118 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9120 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9124 if (action != last_action)
9125 StopSound(element_info[sound_element].sound[last_action]);
9127 if (last_action == ACTION_SLEEPING)
9128 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9132 static void PlayAllPlayersSound()
9136 for (i = 0; i < MAX_PLAYERS; i++)
9137 if (stored_player[i].active)
9138 PlayPlayerSound(&stored_player[i]);
9141 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9143 boolean last_waiting = player->is_waiting;
9144 int move_dir = player->MovDir;
9146 player->dir_waiting = move_dir;
9147 player->last_action_waiting = player->action_waiting;
9151 if (!last_waiting) /* not waiting -> waiting */
9153 player->is_waiting = TRUE;
9155 player->frame_counter_bored =
9157 game.player_boring_delay_fixed +
9158 SimpleRND(game.player_boring_delay_random);
9159 player->frame_counter_sleeping =
9161 game.player_sleeping_delay_fixed +
9162 SimpleRND(game.player_sleeping_delay_random);
9165 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9167 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9171 if (game.player_sleeping_delay_fixed +
9172 game.player_sleeping_delay_random > 0 &&
9173 player->anim_delay_counter == 0 &&
9174 player->post_delay_counter == 0 &&
9175 FrameCounter >= player->frame_counter_sleeping)
9176 player->is_sleeping = TRUE;
9177 else if (game.player_boring_delay_fixed +
9178 game.player_boring_delay_random > 0 &&
9179 FrameCounter >= player->frame_counter_bored)
9180 player->is_bored = TRUE;
9182 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9183 player->is_bored ? ACTION_BORING :
9187 if (player->is_sleeping && player->use_murphy)
9189 /* special case for sleeping Murphy when leaning against non-free tile */
9191 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9192 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9193 !IS_MOVING(player->jx - 1, player->jy)))
9195 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9196 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9197 !IS_MOVING(player->jx + 1, player->jy)))
9198 move_dir = MV_RIGHT;
9200 player->is_sleeping = FALSE;
9202 player->dir_waiting = move_dir;
9206 if (player->is_sleeping)
9208 if (player->num_special_action_sleeping > 0)
9210 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9212 int last_special_action = player->special_action_sleeping;
9213 int num_special_action = player->num_special_action_sleeping;
9214 int special_action =
9215 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9216 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9217 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9218 last_special_action + 1 : ACTION_SLEEPING);
9219 int special_graphic =
9220 el_act_dir2img(player->artwork_element, special_action, move_dir);
9222 player->anim_delay_counter =
9223 graphic_info[special_graphic].anim_delay_fixed +
9224 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9225 player->post_delay_counter =
9226 graphic_info[special_graphic].post_delay_fixed +
9227 SimpleRND(graphic_info[special_graphic].post_delay_random);
9229 player->special_action_sleeping = special_action;
9232 if (player->anim_delay_counter > 0)
9234 player->action_waiting = player->special_action_sleeping;
9235 player->anim_delay_counter--;
9237 else if (player->post_delay_counter > 0)
9239 player->post_delay_counter--;
9243 else if (player->is_bored)
9245 if (player->num_special_action_bored > 0)
9247 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9249 int special_action =
9250 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9251 int special_graphic =
9252 el_act_dir2img(player->artwork_element, special_action, move_dir);
9254 player->anim_delay_counter =
9255 graphic_info[special_graphic].anim_delay_fixed +
9256 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9257 player->post_delay_counter =
9258 graphic_info[special_graphic].post_delay_fixed +
9259 SimpleRND(graphic_info[special_graphic].post_delay_random);
9261 player->special_action_bored = special_action;
9264 if (player->anim_delay_counter > 0)
9266 player->action_waiting = player->special_action_bored;
9267 player->anim_delay_counter--;
9269 else if (player->post_delay_counter > 0)
9271 player->post_delay_counter--;
9276 else if (last_waiting) /* waiting -> not waiting */
9278 player->is_waiting = FALSE;
9279 player->is_bored = FALSE;
9280 player->is_sleeping = FALSE;
9282 player->frame_counter_bored = -1;
9283 player->frame_counter_sleeping = -1;
9285 player->anim_delay_counter = 0;
9286 player->post_delay_counter = 0;
9288 player->dir_waiting = player->MovDir;
9289 player->action_waiting = ACTION_DEFAULT;
9291 player->special_action_bored = ACTION_DEFAULT;
9292 player->special_action_sleeping = ACTION_DEFAULT;
9296 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9298 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9299 int left = player_action & JOY_LEFT;
9300 int right = player_action & JOY_RIGHT;
9301 int up = player_action & JOY_UP;
9302 int down = player_action & JOY_DOWN;
9303 int button1 = player_action & JOY_BUTTON_1;
9304 int button2 = player_action & JOY_BUTTON_2;
9305 int dx = (left ? -1 : right ? 1 : 0);
9306 int dy = (up ? -1 : down ? 1 : 0);
9308 if (!player->active || tape.pausing)
9314 snapped = SnapField(player, dx, dy);
9318 dropped = DropElement(player);
9320 moved = MovePlayer(player, dx, dy);
9323 if (tape.single_step && tape.recording && !tape.pausing)
9325 if (button1 || (dropped && !moved))
9327 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9328 SnapField(player, 0, 0); /* stop snapping */
9332 SetPlayerWaiting(player, FALSE);
9334 return player_action;
9338 /* no actions for this player (no input at player's configured device) */
9340 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9341 SnapField(player, 0, 0);
9342 CheckGravityMovementWhenNotMoving(player);
9344 if (player->MovPos == 0)
9345 SetPlayerWaiting(player, TRUE);
9347 if (player->MovPos == 0) /* needed for tape.playing */
9348 player->is_moving = FALSE;
9350 player->is_dropping = FALSE;
9351 player->is_dropping_pressed = FALSE;
9352 player->drop_pressed_delay = 0;
9358 static void CheckLevelTime()
9362 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9364 if (level.native_em_level->lev->home == 0) /* all players at home */
9366 local_player->LevelSolved = TRUE;
9367 AllPlayersGone = TRUE;
9369 level.native_em_level->lev->home = -1;
9372 if (level.native_em_level->ply[0]->alive == 0 &&
9373 level.native_em_level->ply[1]->alive == 0 &&
9374 level.native_em_level->ply[2]->alive == 0 &&
9375 level.native_em_level->ply[3]->alive == 0) /* all dead */
9376 AllPlayersGone = TRUE;
9379 if (TimeFrames >= FRAMES_PER_SECOND)
9384 for (i = 0; i < MAX_PLAYERS; i++)
9386 struct PlayerInfo *player = &stored_player[i];
9388 if (SHIELD_ON(player))
9390 player->shield_normal_time_left--;
9392 if (player->shield_deadly_time_left > 0)
9393 player->shield_deadly_time_left--;
9397 if (!level.use_step_counter)
9405 if (TimeLeft <= 10 && setup.time_limit)
9406 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9408 DrawGameValue_Time(TimeLeft);
9410 if (!TimeLeft && setup.time_limit)
9412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9413 level.native_em_level->lev->killed_out_of_time = TRUE;
9415 for (i = 0; i < MAX_PLAYERS; i++)
9416 KillPlayer(&stored_player[i]);
9419 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9420 DrawGameValue_Time(TimePlayed);
9422 level.native_em_level->lev->time =
9423 (level.time == 0 ? TimePlayed : TimeLeft);
9426 if (tape.recording || tape.playing)
9427 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9431 void AdvanceFrameAndPlayerCounters(int player_nr)
9436 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9437 FrameCounter, FrameCounter + 1);
9440 /* advance frame counters (global frame counter and time frame counter) */
9444 /* advance player counters (counters for move delay, move animation etc.) */
9445 for (i = 0; i < MAX_PLAYERS; i++)
9447 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9448 int move_delay_value = stored_player[i].move_delay_value;
9449 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9451 if (!advance_player_counters) /* not all players may be affected */
9454 #if USE_NEW_PLAYER_ANIM
9455 if (move_frames == 0) /* less than one move per game frame */
9457 int stepsize = TILEX / move_delay_value;
9458 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9459 int count = (stored_player[i].is_moving ?
9460 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9462 if (count % delay == 0)
9467 stored_player[i].Frame += move_frames;
9469 if (stored_player[i].MovPos != 0)
9470 stored_player[i].StepFrame += move_frames;
9472 if (stored_player[i].move_delay > 0)
9473 stored_player[i].move_delay--;
9475 /* due to bugs in previous versions, counter must count up, not down */
9476 if (stored_player[i].push_delay != -1)
9477 stored_player[i].push_delay++;
9479 if (stored_player[i].drop_delay > 0)
9480 stored_player[i].drop_delay--;
9482 if (stored_player[i].is_dropping_pressed)
9483 stored_player[i].drop_pressed_delay++;
9487 void StartGameActions(boolean init_network_game, boolean record_tape,
9490 unsigned long new_random_seed = InitRND(random_seed);
9493 TapeStartRecording(new_random_seed);
9495 #if defined(NETWORK_AVALIABLE)
9496 if (init_network_game)
9498 SendToServer_StartPlaying();
9506 game_status = GAME_MODE_PLAYING;
9513 static unsigned long game_frame_delay = 0;
9514 unsigned long game_frame_delay_value;
9515 byte *recorded_player_action;
9516 byte summarized_player_action = 0;
9517 byte tape_action[MAX_PLAYERS];
9520 if (game.restart_level)
9521 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9523 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9525 if (level.native_em_level->lev->home == 0) /* all players at home */
9527 local_player->LevelSolved = TRUE;
9528 AllPlayersGone = TRUE;
9530 level.native_em_level->lev->home = -1;
9533 if (level.native_em_level->ply[0]->alive == 0 &&
9534 level.native_em_level->ply[1]->alive == 0 &&
9535 level.native_em_level->ply[2]->alive == 0 &&
9536 level.native_em_level->ply[3]->alive == 0) /* all dead */
9537 AllPlayersGone = TRUE;
9540 if (local_player->LevelSolved)
9543 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9546 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9549 game_frame_delay_value =
9550 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9552 if (tape.playing && tape.warp_forward && !tape.pausing)
9553 game_frame_delay_value = 0;
9555 /* ---------- main game synchronization point ---------- */
9557 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9559 if (network_playing && !network_player_action_received)
9561 /* try to get network player actions in time */
9563 #if defined(NETWORK_AVALIABLE)
9564 /* last chance to get network player actions without main loop delay */
9568 /* game was quit by network peer */
9569 if (game_status != GAME_MODE_PLAYING)
9572 if (!network_player_action_received)
9573 return; /* failed to get network player actions in time */
9575 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9581 /* at this point we know that we really continue executing the game */
9584 network_player_action_received = FALSE;
9587 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9589 if (tape.set_centered_player)
9591 game.centered_player_nr_next = tape.centered_player_nr_next;
9592 game.set_centered_player = TRUE;
9595 for (i = 0; i < MAX_PLAYERS; i++)
9597 summarized_player_action |= stored_player[i].action;
9599 if (!network_playing)
9600 stored_player[i].effective_action = stored_player[i].action;
9603 #if defined(NETWORK_AVALIABLE)
9604 if (network_playing)
9605 SendToServer_MovePlayer(summarized_player_action);
9608 if (!options.network && !setup.team_mode)
9609 local_player->effective_action = summarized_player_action;
9611 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9613 for (i = 0; i < MAX_PLAYERS; i++)
9614 stored_player[i].effective_action =
9615 (i == game.centered_player_nr ? summarized_player_action : 0);
9618 if (recorded_player_action != NULL)
9619 for (i = 0; i < MAX_PLAYERS; i++)
9620 stored_player[i].effective_action = recorded_player_action[i];
9622 for (i = 0; i < MAX_PLAYERS; i++)
9624 tape_action[i] = stored_player[i].effective_action;
9626 /* (this can only happen in the R'n'D game engine) */
9627 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9628 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9631 /* only record actions from input devices, but not programmed actions */
9633 TapeRecordAction(tape_action);
9635 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9637 GameActions_EM_Main();
9645 void GameActions_EM_Main()
9647 byte effective_action[MAX_PLAYERS];
9648 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9651 for (i = 0; i < MAX_PLAYERS; i++)
9652 effective_action[i] = stored_player[i].effective_action;
9654 GameActions_EM(effective_action, warp_mode);
9658 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9661 void GameActions_RND()
9663 int magic_wall_x = 0, magic_wall_y = 0;
9664 int i, x, y, element, graphic;
9666 InitPlayfieldScanModeVars();
9668 #if USE_ONE_MORE_CHANGE_PER_FRAME
9669 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9671 SCAN_PLAYFIELD(x, y)
9673 ChangeCount[x][y] = 0;
9674 ChangeEvent[x][y] = -1;
9680 if (game.set_centered_player)
9682 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9684 /* switching to "all players" only possible if all players fit to screen */
9685 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9687 game.centered_player_nr_next = game.centered_player_nr;
9688 game.set_centered_player = FALSE;
9691 /* do not switch focus to non-existing (or non-active) player */
9692 if (game.centered_player_nr_next >= 0 &&
9693 !stored_player[game.centered_player_nr_next].active)
9695 game.centered_player_nr_next = game.centered_player_nr;
9696 game.set_centered_player = FALSE;
9700 if (game.set_centered_player &&
9701 ScreenMovPos == 0) /* screen currently aligned at tile position */
9705 if (game.centered_player_nr_next == -1)
9707 setScreenCenteredToAllPlayers(&sx, &sy);
9711 sx = stored_player[game.centered_player_nr_next].jx;
9712 sy = stored_player[game.centered_player_nr_next].jy;
9715 game.centered_player_nr = game.centered_player_nr_next;
9716 game.set_centered_player = FALSE;
9718 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9719 DrawGameDoorValues();
9723 for (i = 0; i < MAX_PLAYERS; i++)
9725 int actual_player_action = stored_player[i].effective_action;
9728 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9729 - rnd_equinox_tetrachloride 048
9730 - rnd_equinox_tetrachloride_ii 096
9731 - rnd_emanuel_schmieg 002
9732 - doctor_sloan_ww 001, 020
9734 if (stored_player[i].MovPos == 0)
9735 CheckGravityMovement(&stored_player[i]);
9738 /* overwrite programmed action with tape action */
9739 if (stored_player[i].programmed_action)
9740 actual_player_action = stored_player[i].programmed_action;
9743 PlayerActions(&stored_player[i], actual_player_action);
9745 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9747 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9748 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9751 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9755 network_player_action_received = FALSE;
9758 ScrollScreen(NULL, SCROLL_GO_ON);
9760 /* for backwards compatibility, the following code emulates a fixed bug that
9761 occured when pushing elements (causing elements that just made their last
9762 pushing step to already (if possible) make their first falling step in the
9763 same game frame, which is bad); this code is also needed to use the famous
9764 "spring push bug" which is used in older levels and might be wanted to be
9765 used also in newer levels, but in this case the buggy pushing code is only
9766 affecting the "spring" element and no other elements */
9768 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9770 for (i = 0; i < MAX_PLAYERS; i++)
9772 struct PlayerInfo *player = &stored_player[i];
9776 if (player->active && player->is_pushing && player->is_moving &&
9778 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9779 Feld[x][y] == EL_SPRING))
9781 ContinueMoving(x, y);
9783 /* continue moving after pushing (this is actually a bug) */
9784 if (!IS_MOVING(x, y))
9793 SCAN_PLAYFIELD(x, y)
9795 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9798 ChangeCount[x][y] = 0;
9799 ChangeEvent[x][y] = -1;
9801 /* this must be handled before main playfield loop */
9802 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9805 if (MovDelay[x][y] <= 0)
9809 #if USE_NEW_SNAP_DELAY
9810 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9813 if (MovDelay[x][y] <= 0)
9816 DrawLevelField(x, y);
9818 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9824 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9826 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9827 printf("GameActions(): This should never happen!\n");
9829 ChangePage[x][y] = -1;
9834 if (WasJustMoving[x][y] > 0)
9835 WasJustMoving[x][y]--;
9836 if (WasJustFalling[x][y] > 0)
9837 WasJustFalling[x][y]--;
9838 if (CheckCollision[x][y] > 0)
9839 CheckCollision[x][y]--;
9843 /* reset finished pushing action (not done in ContinueMoving() to allow
9844 continuous pushing animation for elements with zero push delay) */
9845 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9847 ResetGfxAnimation(x, y);
9848 DrawLevelField(x, y);
9852 if (IS_BLOCKED(x, y))
9856 Blocked2Moving(x, y, &oldx, &oldy);
9857 if (!IS_MOVING(oldx, oldy))
9859 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9860 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9861 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9862 printf("GameActions(): This should never happen!\n");
9869 SCAN_PLAYFIELD(x, y)
9871 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9874 element = Feld[x][y];
9875 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9878 printf("::: %d,%d\n", x, y);
9880 if (element == EL_ROCK)
9881 printf("::: Yo man! Rocks can fall!\n");
9885 ResetGfxFrame(x, y, TRUE);
9887 if (graphic_info[graphic].anim_global_sync)
9888 GfxFrame[x][y] = FrameCounter;
9889 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9891 int old_gfx_frame = GfxFrame[x][y];
9893 GfxFrame[x][y] = CustomValue[x][y];
9896 if (GfxFrame[x][y] != old_gfx_frame)
9898 DrawLevelGraphicAnimation(x, y, graphic);
9900 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9902 int old_gfx_frame = GfxFrame[x][y];
9904 GfxFrame[x][y] = element_info[element].collect_score;
9907 if (GfxFrame[x][y] != old_gfx_frame)
9909 DrawLevelGraphicAnimation(x, y, graphic);
9911 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9913 int old_gfx_frame = GfxFrame[x][y];
9915 GfxFrame[x][y] = ChangeDelay[x][y];
9918 if (GfxFrame[x][y] != old_gfx_frame)
9920 DrawLevelGraphicAnimation(x, y, graphic);
9924 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9925 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9926 ResetRandomAnimationValue(x, y);
9928 SetRandomAnimationValue(x, y);
9930 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9932 if (IS_INACTIVE(element))
9934 if (IS_ANIMATED(graphic))
9935 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9940 /* this may take place after moving, so 'element' may have changed */
9941 if (IS_CHANGING(x, y) &&
9942 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9944 int page = element_info[element].event_page_nr[CE_DELAY];
9946 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9950 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9954 if (element == EL_CUSTOM_255)
9955 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9959 HandleElementChange(x, y, page);
9961 if (CAN_CHANGE(element))
9962 HandleElementChange(x, y, page);
9964 if (HAS_ACTION(element))
9965 ExecuteCustomElementAction(x, y, element, page);
9970 element = Feld[x][y];
9971 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9974 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9978 element = Feld[x][y];
9979 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9981 if (IS_ANIMATED(graphic) &&
9984 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9986 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9987 EdelsteinFunkeln(x, y);
9989 else if ((element == EL_ACID ||
9990 element == EL_EXIT_OPEN ||
9991 element == EL_SP_EXIT_OPEN ||
9992 element == EL_SP_TERMINAL ||
9993 element == EL_SP_TERMINAL_ACTIVE ||
9994 element == EL_EXTRA_TIME ||
9995 element == EL_SHIELD_NORMAL ||
9996 element == EL_SHIELD_DEADLY) &&
9997 IS_ANIMATED(graphic))
9998 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9999 else if (IS_MOVING(x, y))
10000 ContinueMoving(x, y);
10001 else if (IS_ACTIVE_BOMB(element))
10002 CheckDynamite(x, y);
10003 else if (element == EL_AMOEBA_GROWING)
10004 AmoebeWaechst(x, y);
10005 else if (element == EL_AMOEBA_SHRINKING)
10006 AmoebaDisappearing(x, y);
10008 #if !USE_NEW_AMOEBA_CODE
10009 else if (IS_AMOEBALIVE(element))
10010 AmoebeAbleger(x, y);
10013 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10015 else if (element == EL_EXIT_CLOSED)
10017 else if (element == EL_SP_EXIT_CLOSED)
10019 else if (element == EL_EXPANDABLE_WALL_GROWING)
10020 MauerWaechst(x, y);
10021 else if (element == EL_EXPANDABLE_WALL ||
10022 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10023 element == EL_EXPANDABLE_WALL_VERTICAL ||
10024 element == EL_EXPANDABLE_WALL_ANY ||
10025 element == EL_BD_EXPANDABLE_WALL)
10026 MauerAbleger(x, y);
10027 else if (element == EL_FLAMES)
10028 CheckForDragon(x, y);
10029 else if (element == EL_EXPLOSION)
10030 ; /* drawing of correct explosion animation is handled separately */
10031 else if (element == EL_ELEMENT_SNAPPING ||
10032 element == EL_DIAGONAL_SHRINKING ||
10033 element == EL_DIAGONAL_GROWING)
10036 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10038 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10041 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10042 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10045 if (element == EL_CUSTOM_255 ||
10046 element == EL_CUSTOM_256)
10047 DrawLevelGraphicAnimation(x, y, graphic);
10050 if (IS_BELT_ACTIVE(element))
10051 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10053 if (game.magic_wall_active)
10055 int jx = local_player->jx, jy = local_player->jy;
10057 /* play the element sound at the position nearest to the player */
10058 if ((element == EL_MAGIC_WALL_FULL ||
10059 element == EL_MAGIC_WALL_ACTIVE ||
10060 element == EL_MAGIC_WALL_EMPTYING ||
10061 element == EL_BD_MAGIC_WALL_FULL ||
10062 element == EL_BD_MAGIC_WALL_ACTIVE ||
10063 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10064 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10072 #if USE_NEW_AMOEBA_CODE
10073 /* new experimental amoeba growth stuff */
10074 if (!(FrameCounter % 8))
10076 static unsigned long random = 1684108901;
10078 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10080 x = RND(lev_fieldx);
10081 y = RND(lev_fieldy);
10082 element = Feld[x][y];
10084 if (!IS_PLAYER(x,y) &&
10085 (element == EL_EMPTY ||
10086 CAN_GROW_INTO(element) ||
10087 element == EL_QUICKSAND_EMPTY ||
10088 element == EL_ACID_SPLASH_LEFT ||
10089 element == EL_ACID_SPLASH_RIGHT))
10091 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10092 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10093 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10094 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10095 Feld[x][y] = EL_AMOEBA_DROP;
10098 random = random * 129 + 1;
10104 if (game.explosions_delayed)
10107 game.explosions_delayed = FALSE;
10110 SCAN_PLAYFIELD(x, y)
10112 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10115 element = Feld[x][y];
10117 if (ExplodeField[x][y])
10118 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10119 else if (element == EL_EXPLOSION)
10120 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10122 ExplodeField[x][y] = EX_TYPE_NONE;
10125 game.explosions_delayed = TRUE;
10128 if (game.magic_wall_active)
10130 if (!(game.magic_wall_time_left % 4))
10132 int element = Feld[magic_wall_x][magic_wall_y];
10134 if (element == EL_BD_MAGIC_WALL_FULL ||
10135 element == EL_BD_MAGIC_WALL_ACTIVE ||
10136 element == EL_BD_MAGIC_WALL_EMPTYING)
10137 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10139 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10142 if (game.magic_wall_time_left > 0)
10144 game.magic_wall_time_left--;
10145 if (!game.magic_wall_time_left)
10148 SCAN_PLAYFIELD(x, y)
10150 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10153 element = Feld[x][y];
10155 if (element == EL_MAGIC_WALL_ACTIVE ||
10156 element == EL_MAGIC_WALL_FULL)
10158 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10159 DrawLevelField(x, y);
10161 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10162 element == EL_BD_MAGIC_WALL_FULL)
10164 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10165 DrawLevelField(x, y);
10169 game.magic_wall_active = FALSE;
10174 if (game.light_time_left > 0)
10176 game.light_time_left--;
10178 if (game.light_time_left == 0)
10179 RedrawAllLightSwitchesAndInvisibleElements();
10182 if (game.timegate_time_left > 0)
10184 game.timegate_time_left--;
10186 if (game.timegate_time_left == 0)
10187 CloseAllOpenTimegates();
10190 if (game.lenses_time_left > 0)
10192 game.lenses_time_left--;
10194 if (game.lenses_time_left == 0)
10195 RedrawAllInvisibleElementsForLenses();
10198 if (game.magnify_time_left > 0)
10200 game.magnify_time_left--;
10202 if (game.magnify_time_left == 0)
10203 RedrawAllInvisibleElementsForMagnifier();
10206 for (i = 0; i < MAX_PLAYERS; i++)
10208 struct PlayerInfo *player = &stored_player[i];
10210 if (SHIELD_ON(player))
10212 if (player->shield_deadly_time_left)
10213 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10214 else if (player->shield_normal_time_left)
10215 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10222 PlayAllPlayersSound();
10224 if (options.debug) /* calculate frames per second */
10226 static unsigned long fps_counter = 0;
10227 static int fps_frames = 0;
10228 unsigned long fps_delay_ms = Counter() - fps_counter;
10232 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10234 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10237 fps_counter = Counter();
10240 redraw_mask |= REDRAW_FPS;
10243 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10245 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10247 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10249 local_player->show_envelope = 0;
10252 /* use random number generator in every frame to make it less predictable */
10253 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10257 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10259 int min_x = x, min_y = y, max_x = x, max_y = y;
10262 for (i = 0; i < MAX_PLAYERS; i++)
10264 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10266 if (!stored_player[i].active || &stored_player[i] == player)
10269 min_x = MIN(min_x, jx);
10270 min_y = MIN(min_y, jy);
10271 max_x = MAX(max_x, jx);
10272 max_y = MAX(max_y, jy);
10275 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10278 static boolean AllPlayersInVisibleScreen()
10282 for (i = 0; i < MAX_PLAYERS; i++)
10284 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10286 if (!stored_player[i].active)
10289 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10296 void ScrollLevel(int dx, int dy)
10298 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10301 BlitBitmap(drawto_field, drawto_field,
10302 FX + TILEX * (dx == -1) - softscroll_offset,
10303 FY + TILEY * (dy == -1) - softscroll_offset,
10304 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10305 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10306 FX + TILEX * (dx == 1) - softscroll_offset,
10307 FY + TILEY * (dy == 1) - softscroll_offset);
10311 x = (dx == 1 ? BX1 : BX2);
10312 for (y = BY1; y <= BY2; y++)
10313 DrawScreenField(x, y);
10318 y = (dy == 1 ? BY1 : BY2);
10319 for (x = BX1; x <= BX2; x++)
10320 DrawScreenField(x, y);
10323 redraw_mask |= REDRAW_FIELD;
10326 static boolean canFallDown(struct PlayerInfo *player)
10328 int jx = player->jx, jy = player->jy;
10330 return (IN_LEV_FIELD(jx, jy + 1) &&
10331 (IS_FREE(jx, jy + 1) ||
10332 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10333 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10334 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10337 static boolean canPassField(int x, int y, int move_dir)
10339 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10340 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10341 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10342 int nextx = x + dx;
10343 int nexty = y + dy;
10344 int element = Feld[x][y];
10346 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10347 !CAN_MOVE(element) &&
10348 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10349 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10350 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10353 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10355 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10356 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10357 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10361 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10362 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10363 (IS_DIGGABLE(Feld[newx][newy]) ||
10364 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10365 canPassField(newx, newy, move_dir)));
10368 static void CheckGravityMovement(struct PlayerInfo *player)
10370 #if USE_PLAYER_GRAVITY
10371 if (player->gravity && !player->programmed_action)
10373 if (game.gravity && !player->programmed_action)
10376 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10377 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10378 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10379 int jx = player->jx, jy = player->jy;
10380 boolean player_is_moving_to_valid_field =
10381 (!player_is_snapping &&
10382 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10383 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10384 boolean player_can_fall_down = canFallDown(player);
10386 if (player_can_fall_down &&
10387 !player_is_moving_to_valid_field)
10388 player->programmed_action = MV_DOWN;
10392 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10394 return CheckGravityMovement(player);
10396 #if USE_PLAYER_GRAVITY
10397 if (player->gravity && !player->programmed_action)
10399 if (game.gravity && !player->programmed_action)
10402 int jx = player->jx, jy = player->jy;
10403 boolean field_under_player_is_free =
10404 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10405 boolean player_is_standing_on_valid_field =
10406 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10407 (IS_WALKABLE(Feld[jx][jy]) &&
10408 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10410 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10411 player->programmed_action = MV_DOWN;
10416 MovePlayerOneStep()
10417 -----------------------------------------------------------------------------
10418 dx, dy: direction (non-diagonal) to try to move the player to
10419 real_dx, real_dy: direction as read from input device (can be diagonal)
10422 boolean MovePlayerOneStep(struct PlayerInfo *player,
10423 int dx, int dy, int real_dx, int real_dy)
10425 int jx = player->jx, jy = player->jy;
10426 int new_jx = jx + dx, new_jy = jy + dy;
10427 #if !USE_FIXED_DONT_RUN_INTO
10431 boolean player_can_move = !player->cannot_move;
10433 if (!player->active || (!dx && !dy))
10434 return MP_NO_ACTION;
10436 player->MovDir = (dx < 0 ? MV_LEFT :
10437 dx > 0 ? MV_RIGHT :
10439 dy > 0 ? MV_DOWN : MV_NONE);
10441 if (!IN_LEV_FIELD(new_jx, new_jy))
10442 return MP_NO_ACTION;
10444 if (!player_can_move)
10447 if (player->MovPos == 0)
10449 player->is_moving = FALSE;
10450 player->is_digging = FALSE;
10451 player->is_collecting = FALSE;
10452 player->is_snapping = FALSE;
10453 player->is_pushing = FALSE;
10456 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10457 SnapField(player, 0, 0);
10461 return MP_NO_ACTION;
10466 if (!options.network && game.centered_player_nr == -1 &&
10467 !AllPlayersInSight(player, new_jx, new_jy))
10468 return MP_NO_ACTION;
10470 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10471 return MP_NO_ACTION;
10474 #if !USE_FIXED_DONT_RUN_INTO
10475 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10477 /* (moved to DigField()) */
10478 if (player_can_move && DONT_RUN_INTO(element))
10480 if (element == EL_ACID && dx == 0 && dy == 1)
10482 SplashAcid(new_jx, new_jy);
10483 Feld[jx][jy] = EL_PLAYER_1;
10484 InitMovingField(jx, jy, MV_DOWN);
10485 Store[jx][jy] = EL_ACID;
10486 ContinueMoving(jx, jy);
10487 BuryPlayer(player);
10490 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10496 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10498 #if USE_FIXED_DONT_RUN_INTO
10499 if (can_move == MP_DONT_RUN_INTO)
10503 if (can_move != MP_MOVING)
10506 #if USE_FIXED_DONT_RUN_INTO
10509 /* check if DigField() has caused relocation of the player */
10510 if (player->jx != jx || player->jy != jy)
10511 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10513 StorePlayer[jx][jy] = 0;
10514 player->last_jx = jx;
10515 player->last_jy = jy;
10516 player->jx = new_jx;
10517 player->jy = new_jy;
10518 StorePlayer[new_jx][new_jy] = player->element_nr;
10520 if (player->move_delay_value_next != -1)
10522 player->move_delay_value = player->move_delay_value_next;
10523 player->move_delay_value_next = -1;
10527 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10529 player->step_counter++;
10531 PlayerVisit[jx][jy] = FrameCounter;
10533 ScrollPlayer(player, SCROLL_INIT);
10538 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10540 int jx = player->jx, jy = player->jy;
10541 int old_jx = jx, old_jy = jy;
10542 int moved = MP_NO_ACTION;
10544 if (!player->active)
10549 if (player->MovPos == 0)
10551 player->is_moving = FALSE;
10552 player->is_digging = FALSE;
10553 player->is_collecting = FALSE;
10554 player->is_snapping = FALSE;
10555 player->is_pushing = FALSE;
10561 if (player->move_delay > 0)
10564 player->move_delay = -1; /* set to "uninitialized" value */
10566 /* store if player is automatically moved to next field */
10567 player->is_auto_moving = (player->programmed_action != MV_NONE);
10569 /* remove the last programmed player action */
10570 player->programmed_action = 0;
10572 if (player->MovPos)
10574 /* should only happen if pre-1.2 tape recordings are played */
10575 /* this is only for backward compatibility */
10577 int original_move_delay_value = player->move_delay_value;
10580 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10584 /* scroll remaining steps with finest movement resolution */
10585 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10587 while (player->MovPos)
10589 ScrollPlayer(player, SCROLL_GO_ON);
10590 ScrollScreen(NULL, SCROLL_GO_ON);
10592 AdvanceFrameAndPlayerCounters(player->index_nr);
10598 player->move_delay_value = original_move_delay_value;
10601 player->is_active = FALSE;
10603 if (player->last_move_dir & MV_HORIZONTAL)
10605 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10606 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10610 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10611 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10614 #if USE_FIXED_BORDER_RUNNING_GFX
10615 if (!moved && !player->is_active)
10617 player->is_moving = FALSE;
10618 player->is_digging = FALSE;
10619 player->is_collecting = FALSE;
10620 player->is_snapping = FALSE;
10621 player->is_pushing = FALSE;
10629 if (moved & MP_MOVING && !ScreenMovPos &&
10630 (player->index_nr == game.centered_player_nr ||
10631 game.centered_player_nr == -1))
10633 if (moved & MP_MOVING && !ScreenMovPos &&
10634 (player == local_player || !options.network))
10637 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10638 int offset = (setup.scroll_delay ? 3 : 0);
10640 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10642 /* actual player has left the screen -- scroll in that direction */
10643 if (jx != old_jx) /* player has moved horizontally */
10644 scroll_x += (jx - old_jx);
10645 else /* player has moved vertically */
10646 scroll_y += (jy - old_jy);
10650 if (jx != old_jx) /* player has moved horizontally */
10652 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10653 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10654 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10656 /* don't scroll over playfield boundaries */
10657 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10658 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10660 /* don't scroll more than one field at a time */
10661 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10663 /* don't scroll against the player's moving direction */
10664 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10665 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10666 scroll_x = old_scroll_x;
10668 else /* player has moved vertically */
10670 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10671 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10672 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10674 /* don't scroll over playfield boundaries */
10675 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10676 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10678 /* don't scroll more than one field at a time */
10679 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10681 /* don't scroll against the player's moving direction */
10682 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10683 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10684 scroll_y = old_scroll_y;
10688 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10691 if (!options.network && game.centered_player_nr == -1 &&
10692 !AllPlayersInVisibleScreen())
10694 scroll_x = old_scroll_x;
10695 scroll_y = old_scroll_y;
10699 if (!options.network && !AllPlayersInVisibleScreen())
10701 scroll_x = old_scroll_x;
10702 scroll_y = old_scroll_y;
10707 ScrollScreen(player, SCROLL_INIT);
10708 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10713 player->StepFrame = 0;
10715 if (moved & MP_MOVING)
10717 if (old_jx != jx && old_jy == jy)
10718 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10719 else if (old_jx == jx && old_jy != jy)
10720 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10722 DrawLevelField(jx, jy); /* for "crumbled sand" */
10724 player->last_move_dir = player->MovDir;
10725 player->is_moving = TRUE;
10726 player->is_snapping = FALSE;
10727 player->is_switching = FALSE;
10728 player->is_dropping = FALSE;
10729 player->is_dropping_pressed = FALSE;
10730 player->drop_pressed_delay = 0;
10734 CheckGravityMovementWhenNotMoving(player);
10736 player->is_moving = FALSE;
10738 /* at this point, the player is allowed to move, but cannot move right now
10739 (e.g. because of something blocking the way) -- ensure that the player
10740 is also allowed to move in the next frame (in old versions before 3.1.1,
10741 the player was forced to wait again for eight frames before next try) */
10743 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10744 player->move_delay = 0; /* allow direct movement in the next frame */
10747 if (player->move_delay == -1) /* not yet initialized by DigField() */
10748 player->move_delay = player->move_delay_value;
10750 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10752 TestIfPlayerTouchesBadThing(jx, jy);
10753 TestIfPlayerTouchesCustomElement(jx, jy);
10756 if (!player->active)
10757 RemovePlayer(player);
10762 void ScrollPlayer(struct PlayerInfo *player, int mode)
10764 int jx = player->jx, jy = player->jy;
10765 int last_jx = player->last_jx, last_jy = player->last_jy;
10766 int move_stepsize = TILEX / player->move_delay_value;
10768 #if USE_NEW_PLAYER_SPEED
10769 if (!player->active)
10772 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10775 if (!player->active || player->MovPos == 0)
10779 if (mode == SCROLL_INIT)
10781 player->actual_frame_counter = FrameCounter;
10782 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10784 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10785 Feld[last_jx][last_jy] == EL_EMPTY)
10787 int last_field_block_delay = 0; /* start with no blocking at all */
10788 int block_delay_adjustment = player->block_delay_adjustment;
10790 /* if player blocks last field, add delay for exactly one move */
10791 if (player->block_last_field)
10793 last_field_block_delay += player->move_delay_value;
10795 /* when blocking enabled, prevent moving up despite gravity */
10796 #if USE_PLAYER_GRAVITY
10797 if (player->gravity && player->MovDir == MV_UP)
10798 block_delay_adjustment = -1;
10800 if (game.gravity && player->MovDir == MV_UP)
10801 block_delay_adjustment = -1;
10805 /* add block delay adjustment (also possible when not blocking) */
10806 last_field_block_delay += block_delay_adjustment;
10808 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10809 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10812 #if USE_NEW_PLAYER_SPEED
10813 if (player->MovPos != 0) /* player has not yet reached destination */
10819 else if (!FrameReached(&player->actual_frame_counter, 1))
10823 printf("::: player->MovPos: %d -> %d\n",
10825 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10828 #if USE_NEW_PLAYER_SPEED
10829 if (player->MovPos != 0)
10831 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10832 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10834 /* before DrawPlayer() to draw correct player graphic for this case */
10835 if (player->MovPos == 0)
10836 CheckGravityMovement(player);
10839 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10840 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10842 /* before DrawPlayer() to draw correct player graphic for this case */
10843 if (player->MovPos == 0)
10844 CheckGravityMovement(player);
10847 if (player->MovPos == 0) /* player reached destination field */
10850 printf("::: player reached destination field\n");
10853 if (player->move_delay_reset_counter > 0)
10855 player->move_delay_reset_counter--;
10857 if (player->move_delay_reset_counter == 0)
10859 /* continue with normal speed after quickly moving through gate */
10860 HALVE_PLAYER_SPEED(player);
10862 /* be able to make the next move without delay */
10863 player->move_delay = 0;
10867 player->last_jx = jx;
10868 player->last_jy = jy;
10870 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10871 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10872 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10874 DrawPlayer(player); /* needed here only to cleanup last field */
10875 RemovePlayer(player);
10877 if (local_player->friends_still_needed == 0 ||
10878 IS_SP_ELEMENT(Feld[jx][jy]))
10879 player->LevelSolved = player->GameOver = TRUE;
10882 /* this breaks one level: "machine", level 000 */
10884 int move_direction = player->MovDir;
10885 int enter_side = MV_DIR_OPPOSITE(move_direction);
10886 int leave_side = move_direction;
10887 int old_jx = last_jx;
10888 int old_jy = last_jy;
10889 int old_element = Feld[old_jx][old_jy];
10890 int new_element = Feld[jx][jy];
10892 if (IS_CUSTOM_ELEMENT(old_element))
10893 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10895 player->index_bit, leave_side);
10897 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10898 CE_PLAYER_LEAVES_X,
10899 player->index_bit, leave_side);
10901 if (IS_CUSTOM_ELEMENT(new_element))
10902 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10903 player->index_bit, enter_side);
10905 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10906 CE_PLAYER_ENTERS_X,
10907 player->index_bit, enter_side);
10909 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10910 CE_MOVE_OF_X, move_direction);
10913 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10915 TestIfPlayerTouchesBadThing(jx, jy);
10916 TestIfPlayerTouchesCustomElement(jx, jy);
10918 /* needed because pushed element has not yet reached its destination,
10919 so it would trigger a change event at its previous field location */
10920 if (!player->is_pushing)
10921 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10923 if (!player->active)
10924 RemovePlayer(player);
10927 if (level.use_step_counter)
10937 if (TimeLeft <= 10 && setup.time_limit)
10938 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10940 DrawGameValue_Time(TimeLeft);
10942 if (!TimeLeft && setup.time_limit)
10943 for (i = 0; i < MAX_PLAYERS; i++)
10944 KillPlayer(&stored_player[i]);
10946 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10947 DrawGameValue_Time(TimePlayed);
10950 if (tape.single_step && tape.recording && !tape.pausing &&
10951 !player->programmed_action)
10952 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10956 void ScrollScreen(struct PlayerInfo *player, int mode)
10958 static unsigned long screen_frame_counter = 0;
10960 if (mode == SCROLL_INIT)
10962 /* set scrolling step size according to actual player's moving speed */
10963 ScrollStepSize = TILEX / player->move_delay_value;
10965 screen_frame_counter = FrameCounter;
10966 ScreenMovDir = player->MovDir;
10967 ScreenMovPos = player->MovPos;
10968 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10971 else if (!FrameReached(&screen_frame_counter, 1))
10976 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10977 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10978 redraw_mask |= REDRAW_FIELD;
10981 ScreenMovDir = MV_NONE;
10984 void TestIfPlayerTouchesCustomElement(int x, int y)
10986 static int xy[4][2] =
10993 static int trigger_sides[4][2] =
10995 /* center side border side */
10996 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10997 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10998 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10999 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11001 static int touch_dir[4] =
11003 MV_LEFT | MV_RIGHT,
11008 int center_element = Feld[x][y]; /* should always be non-moving! */
11011 for (i = 0; i < NUM_DIRECTIONS; i++)
11013 int xx = x + xy[i][0];
11014 int yy = y + xy[i][1];
11015 int center_side = trigger_sides[i][0];
11016 int border_side = trigger_sides[i][1];
11017 int border_element;
11019 if (!IN_LEV_FIELD(xx, yy))
11022 if (IS_PLAYER(x, y))
11024 struct PlayerInfo *player = PLAYERINFO(x, y);
11026 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11027 border_element = Feld[xx][yy]; /* may be moving! */
11028 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11029 border_element = Feld[xx][yy];
11030 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11031 border_element = MovingOrBlocked2Element(xx, yy);
11033 continue; /* center and border element do not touch */
11035 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11036 player->index_bit, border_side);
11037 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11038 CE_PLAYER_TOUCHES_X,
11039 player->index_bit, border_side);
11041 else if (IS_PLAYER(xx, yy))
11043 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11045 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11047 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11048 continue; /* center and border element do not touch */
11051 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11052 player->index_bit, center_side);
11053 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11054 CE_PLAYER_TOUCHES_X,
11055 player->index_bit, center_side);
11061 #if USE_ELEMENT_TOUCHING_BUGFIX
11063 void TestIfElementTouchesCustomElement(int x, int y)
11065 static int xy[4][2] =
11072 static int trigger_sides[4][2] =
11074 /* center side border side */
11075 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11076 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11077 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11078 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11080 static int touch_dir[4] =
11082 MV_LEFT | MV_RIGHT,
11087 boolean change_center_element = FALSE;
11088 int center_element = Feld[x][y]; /* should always be non-moving! */
11089 int border_element_old[NUM_DIRECTIONS];
11092 for (i = 0; i < NUM_DIRECTIONS; i++)
11094 int xx = x + xy[i][0];
11095 int yy = y + xy[i][1];
11096 int border_element;
11098 border_element_old[i] = -1;
11100 if (!IN_LEV_FIELD(xx, yy))
11103 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11104 border_element = Feld[xx][yy]; /* may be moving! */
11105 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11106 border_element = Feld[xx][yy];
11107 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11108 border_element = MovingOrBlocked2Element(xx, yy);
11110 continue; /* center and border element do not touch */
11112 border_element_old[i] = border_element;
11115 for (i = 0; i < NUM_DIRECTIONS; i++)
11117 int xx = x + xy[i][0];
11118 int yy = y + xy[i][1];
11119 int center_side = trigger_sides[i][0];
11120 int border_element = border_element_old[i];
11122 if (border_element == -1)
11125 /* check for change of border element */
11126 CheckElementChangeBySide(xx, yy, border_element, center_element,
11127 CE_TOUCHING_X, center_side);
11130 for (i = 0; i < NUM_DIRECTIONS; i++)
11132 int border_side = trigger_sides[i][1];
11133 int border_element = border_element_old[i];
11135 if (border_element == -1)
11138 /* check for change of center element (but change it only once) */
11139 if (!change_center_element)
11140 change_center_element =
11141 CheckElementChangeBySide(x, y, center_element, border_element,
11142 CE_TOUCHING_X, border_side);
11148 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11150 static int xy[4][2] =
11157 static int trigger_sides[4][2] =
11159 /* center side border side */
11160 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11161 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11162 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11163 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11165 static int touch_dir[4] =
11167 MV_LEFT | MV_RIGHT,
11172 boolean change_center_element = FALSE;
11173 int center_element = Feld[x][y]; /* should always be non-moving! */
11176 for (i = 0; i < NUM_DIRECTIONS; i++)
11178 int xx = x + xy[i][0];
11179 int yy = y + xy[i][1];
11180 int center_side = trigger_sides[i][0];
11181 int border_side = trigger_sides[i][1];
11182 int border_element;
11184 if (!IN_LEV_FIELD(xx, yy))
11187 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11188 border_element = Feld[xx][yy]; /* may be moving! */
11189 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11190 border_element = Feld[xx][yy];
11191 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11192 border_element = MovingOrBlocked2Element(xx, yy);
11194 continue; /* center and border element do not touch */
11196 /* check for change of center element (but change it only once) */
11197 if (!change_center_element)
11198 change_center_element =
11199 CheckElementChangeBySide(x, y, center_element, border_element,
11200 CE_TOUCHING_X, border_side);
11202 /* check for change of border element */
11203 CheckElementChangeBySide(xx, yy, border_element, center_element,
11204 CE_TOUCHING_X, center_side);
11210 void TestIfElementHitsCustomElement(int x, int y, int direction)
11212 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11213 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11214 int hitx = x + dx, hity = y + dy;
11215 int hitting_element = Feld[x][y];
11216 int touched_element;
11218 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11221 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11222 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11224 if (IN_LEV_FIELD(hitx, hity))
11226 int opposite_direction = MV_DIR_OPPOSITE(direction);
11227 int hitting_side = direction;
11228 int touched_side = opposite_direction;
11229 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11230 MovDir[hitx][hity] != direction ||
11231 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11237 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11238 CE_HITTING_X, touched_side);
11240 CheckElementChangeBySide(hitx, hity, touched_element,
11241 hitting_element, CE_HIT_BY_X, hitting_side);
11243 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11244 CE_HIT_BY_SOMETHING, opposite_direction);
11248 /* "hitting something" is also true when hitting the playfield border */
11249 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11250 CE_HITTING_SOMETHING, direction);
11254 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11256 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11257 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11258 int hitx = x + dx, hity = y + dy;
11259 int hitting_element = Feld[x][y];
11260 int touched_element;
11262 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11263 !IS_FREE(hitx, hity) &&
11264 (!IS_MOVING(hitx, hity) ||
11265 MovDir[hitx][hity] != direction ||
11266 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11269 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11273 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11277 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11278 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11280 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11281 EP_CAN_SMASH_EVERYTHING, direction);
11283 if (IN_LEV_FIELD(hitx, hity))
11285 int opposite_direction = MV_DIR_OPPOSITE(direction);
11286 int hitting_side = direction;
11287 int touched_side = opposite_direction;
11289 int touched_element = MovingOrBlocked2Element(hitx, hity);
11292 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11293 MovDir[hitx][hity] != direction ||
11294 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11303 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11304 CE_SMASHED_BY_SOMETHING, opposite_direction);
11306 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11307 CE_OTHER_IS_SMASHING, touched_side);
11309 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11310 CE_OTHER_GETS_SMASHED, hitting_side);
11316 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11318 int i, kill_x = -1, kill_y = -1;
11320 int bad_element = -1;
11321 static int test_xy[4][2] =
11328 static int test_dir[4] =
11336 for (i = 0; i < NUM_DIRECTIONS; i++)
11338 int test_x, test_y, test_move_dir, test_element;
11340 test_x = good_x + test_xy[i][0];
11341 test_y = good_y + test_xy[i][1];
11343 if (!IN_LEV_FIELD(test_x, test_y))
11347 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11349 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11351 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11352 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11354 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11355 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11359 bad_element = test_element;
11365 if (kill_x != -1 || kill_y != -1)
11367 if (IS_PLAYER(good_x, good_y))
11369 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11371 if (player->shield_deadly_time_left > 0 &&
11372 !IS_INDESTRUCTIBLE(bad_element))
11373 Bang(kill_x, kill_y);
11374 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11375 KillPlayer(player);
11378 Bang(good_x, good_y);
11382 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11384 int i, kill_x = -1, kill_y = -1;
11385 int bad_element = Feld[bad_x][bad_y];
11386 static int test_xy[4][2] =
11393 static int touch_dir[4] =
11395 MV_LEFT | MV_RIGHT,
11400 static int test_dir[4] =
11408 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11411 for (i = 0; i < NUM_DIRECTIONS; i++)
11413 int test_x, test_y, test_move_dir, test_element;
11415 test_x = bad_x + test_xy[i][0];
11416 test_y = bad_y + test_xy[i][1];
11417 if (!IN_LEV_FIELD(test_x, test_y))
11421 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11423 test_element = Feld[test_x][test_y];
11425 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11426 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11428 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11429 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11431 /* good thing is player or penguin that does not move away */
11432 if (IS_PLAYER(test_x, test_y))
11434 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11436 if (bad_element == EL_ROBOT && player->is_moving)
11437 continue; /* robot does not kill player if he is moving */
11439 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11441 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11442 continue; /* center and border element do not touch */
11449 else if (test_element == EL_PENGUIN)
11458 if (kill_x != -1 || kill_y != -1)
11460 if (IS_PLAYER(kill_x, kill_y))
11462 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11464 if (player->shield_deadly_time_left > 0 &&
11465 !IS_INDESTRUCTIBLE(bad_element))
11466 Bang(bad_x, bad_y);
11467 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11468 KillPlayer(player);
11471 Bang(kill_x, kill_y);
11475 void TestIfPlayerTouchesBadThing(int x, int y)
11477 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11480 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11482 TestIfGoodThingHitsBadThing(x, y, move_dir);
11485 void TestIfBadThingTouchesPlayer(int x, int y)
11487 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11490 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11492 TestIfBadThingHitsGoodThing(x, y, move_dir);
11495 void TestIfFriendTouchesBadThing(int x, int y)
11497 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11500 void TestIfBadThingTouchesFriend(int x, int y)
11502 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11505 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11507 int i, kill_x = bad_x, kill_y = bad_y;
11508 static int xy[4][2] =
11516 for (i = 0; i < NUM_DIRECTIONS; i++)
11520 x = bad_x + xy[i][0];
11521 y = bad_y + xy[i][1];
11522 if (!IN_LEV_FIELD(x, y))
11525 element = Feld[x][y];
11526 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11527 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11535 if (kill_x != bad_x || kill_y != bad_y)
11536 Bang(bad_x, bad_y);
11539 void KillPlayer(struct PlayerInfo *player)
11541 int jx = player->jx, jy = player->jy;
11543 if (!player->active)
11546 /* remove accessible field at the player's position */
11547 Feld[jx][jy] = EL_EMPTY;
11549 /* deactivate shield (else Bang()/Explode() would not work right) */
11550 player->shield_normal_time_left = 0;
11551 player->shield_deadly_time_left = 0;
11554 BuryPlayer(player);
11557 static void KillPlayerUnlessEnemyProtected(int x, int y)
11559 if (!PLAYER_ENEMY_PROTECTED(x, y))
11560 KillPlayer(PLAYERINFO(x, y));
11563 static void KillPlayerUnlessExplosionProtected(int x, int y)
11565 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11566 KillPlayer(PLAYERINFO(x, y));
11569 void BuryPlayer(struct PlayerInfo *player)
11571 int jx = player->jx, jy = player->jy;
11573 if (!player->active)
11576 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11577 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11579 player->GameOver = TRUE;
11580 RemovePlayer(player);
11583 void RemovePlayer(struct PlayerInfo *player)
11585 int jx = player->jx, jy = player->jy;
11586 int i, found = FALSE;
11588 player->present = FALSE;
11589 player->active = FALSE;
11591 if (!ExplodeField[jx][jy])
11592 StorePlayer[jx][jy] = 0;
11594 if (player->is_moving)
11595 DrawLevelField(player->last_jx, player->last_jy);
11597 for (i = 0; i < MAX_PLAYERS; i++)
11598 if (stored_player[i].active)
11602 AllPlayersGone = TRUE;
11608 #if USE_NEW_SNAP_DELAY
11609 static void setFieldForSnapping(int x, int y, int element, int direction)
11611 struct ElementInfo *ei = &element_info[element];
11612 int direction_bit = MV_DIR_TO_BIT(direction);
11613 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11614 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11615 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11617 Feld[x][y] = EL_ELEMENT_SNAPPING;
11618 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11620 ResetGfxAnimation(x, y);
11622 GfxElement[x][y] = element;
11623 GfxAction[x][y] = action;
11624 GfxDir[x][y] = direction;
11625 GfxFrame[x][y] = -1;
11630 =============================================================================
11631 checkDiagonalPushing()
11632 -----------------------------------------------------------------------------
11633 check if diagonal input device direction results in pushing of object
11634 (by checking if the alternative direction is walkable, diggable, ...)
11635 =============================================================================
11638 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11639 int x, int y, int real_dx, int real_dy)
11641 int jx, jy, dx, dy, xx, yy;
11643 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11646 /* diagonal direction: check alternative direction */
11651 xx = jx + (dx == 0 ? real_dx : 0);
11652 yy = jy + (dy == 0 ? real_dy : 0);
11654 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11658 =============================================================================
11660 -----------------------------------------------------------------------------
11661 x, y: field next to player (non-diagonal) to try to dig to
11662 real_dx, real_dy: direction as read from input device (can be diagonal)
11663 =============================================================================
11666 int DigField(struct PlayerInfo *player,
11667 int oldx, int oldy, int x, int y,
11668 int real_dx, int real_dy, int mode)
11670 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11671 boolean player_was_pushing = player->is_pushing;
11672 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11673 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11674 int jx = oldx, jy = oldy;
11675 int dx = x - jx, dy = y - jy;
11676 int nextx = x + dx, nexty = y + dy;
11677 int move_direction = (dx == -1 ? MV_LEFT :
11678 dx == +1 ? MV_RIGHT :
11680 dy == +1 ? MV_DOWN : MV_NONE);
11681 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11682 int dig_side = MV_DIR_OPPOSITE(move_direction);
11683 int old_element = Feld[jx][jy];
11684 #if USE_FIXED_DONT_RUN_INTO
11685 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11691 if (is_player) /* function can also be called by EL_PENGUIN */
11693 if (player->MovPos == 0)
11695 player->is_digging = FALSE;
11696 player->is_collecting = FALSE;
11699 if (player->MovPos == 0) /* last pushing move finished */
11700 player->is_pushing = FALSE;
11702 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11704 player->is_switching = FALSE;
11705 player->push_delay = -1;
11707 return MP_NO_ACTION;
11711 #if !USE_FIXED_DONT_RUN_INTO
11712 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11713 return MP_NO_ACTION;
11716 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11717 old_element = Back[jx][jy];
11719 /* in case of element dropped at player position, check background */
11720 else if (Back[jx][jy] != EL_EMPTY &&
11721 game.engine_version >= VERSION_IDENT(2,2,0,0))
11722 old_element = Back[jx][jy];
11724 /* checking here causes player to move into acid even if the current field
11725 cannot be left to that direction */
11727 #if USE_FIXED_DONT_RUN_INTO
11728 if (player_can_move && DONT_RUN_INTO(element))
11730 if (element == EL_ACID && dx == 0 && dy == 1)
11733 Feld[jx][jy] = EL_PLAYER_1;
11734 InitMovingField(jx, jy, MV_DOWN);
11735 Store[jx][jy] = EL_ACID;
11736 ContinueMoving(jx, jy);
11737 BuryPlayer(player);
11740 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11742 return MP_DONT_RUN_INTO;
11747 #if 1 /* ------------------------------ NEW ------------------------------ */
11749 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11750 return MP_NO_ACTION; /* field has no opening in this direction */
11752 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11753 return MP_NO_ACTION; /* field has no opening in this direction */
11755 #if USE_FIXED_DONT_RUN_INTO
11756 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11760 Feld[jx][jy] = player->artwork_element;
11762 Feld[jx][jy] = EL_PLAYER_1;
11764 InitMovingField(jx, jy, MV_DOWN);
11765 Store[jx][jy] = EL_ACID;
11766 ContinueMoving(jx, jy);
11767 BuryPlayer(player);
11769 return MP_DONT_RUN_INTO;
11773 #if USE_FIXED_DONT_RUN_INTO
11774 if (player_can_move && DONT_RUN_INTO(element))
11776 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11778 return MP_DONT_RUN_INTO;
11782 #else /* ------------------------------ OLD ------------------------------ */
11785 #if USE_FIXED_DONT_RUN_INTO
11786 if (player_can_move && DONT_RUN_INTO(element))
11788 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11790 return MP_DONT_RUN_INTO;
11795 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11796 return MP_NO_ACTION; /* field has no opening in this direction */
11798 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11799 return MP_NO_ACTION; /* field has no opening in this direction */
11801 /* checking here causes player to explode when moving into acid */
11803 #if USE_FIXED_DONT_RUN_INTO
11804 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11807 Feld[jx][jy] = EL_PLAYER_1;
11808 InitMovingField(jx, jy, MV_DOWN);
11809 Store[jx][jy] = EL_ACID;
11810 ContinueMoving(jx, jy);
11811 BuryPlayer(player);
11813 return MP_DONT_RUN_INTO;
11818 #endif /* ------------------------------ END ------------------------------ */
11821 #if USE_FIXED_DONT_RUN_INTO
11822 if (player_can_move && DONT_RUN_INTO(element))
11824 if (element == EL_ACID && dx == 0 && dy == 1)
11827 Feld[jx][jy] = EL_PLAYER_1;
11828 InitMovingField(jx, jy, MV_DOWN);
11829 Store[jx][jy] = EL_ACID;
11830 ContinueMoving(jx, jy);
11831 BuryPlayer(player);
11834 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11836 return MP_DONT_RUN_INTO;
11841 #if USE_FIXED_DONT_RUN_INTO
11842 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11843 return MP_NO_ACTION;
11846 #if !USE_FIXED_DONT_RUN_INTO
11847 element = Feld[x][y];
11850 collect_count = element_info[element].collect_count_initial;
11852 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11853 return MP_NO_ACTION;
11855 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11856 player_can_move = player_can_move_or_snap;
11858 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11859 game.engine_version >= VERSION_IDENT(2,2,0,0))
11861 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11862 player->index_bit, dig_side);
11863 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11864 player->index_bit, dig_side);
11866 if (Feld[x][y] != element) /* field changed by snapping */
11869 return MP_NO_ACTION;
11872 #if USE_PLAYER_GRAVITY
11873 if (player->gravity && is_player && !player->is_auto_moving &&
11874 canFallDown(player) && move_direction != MV_DOWN &&
11875 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11876 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11878 if (game.gravity && is_player && !player->is_auto_moving &&
11879 canFallDown(player) && move_direction != MV_DOWN &&
11880 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11881 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11884 if (player_can_move &&
11885 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11887 int sound_element = SND_ELEMENT(element);
11888 int sound_action = ACTION_WALKING;
11890 if (IS_RND_GATE(element))
11892 if (!player->key[RND_GATE_NR(element)])
11893 return MP_NO_ACTION;
11895 else if (IS_RND_GATE_GRAY(element))
11897 if (!player->key[RND_GATE_GRAY_NR(element)])
11898 return MP_NO_ACTION;
11900 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11902 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11903 return MP_NO_ACTION;
11905 else if (element == EL_EXIT_OPEN ||
11906 element == EL_SP_EXIT_OPEN ||
11907 element == EL_SP_EXIT_OPENING)
11909 sound_action = ACTION_PASSING; /* player is passing exit */
11911 else if (element == EL_EMPTY)
11913 sound_action = ACTION_MOVING; /* nothing to walk on */
11916 /* play sound from background or player, whatever is available */
11917 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11918 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11920 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11922 else if (player_can_move &&
11923 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11925 if (!ACCESS_FROM(element, opposite_direction))
11926 return MP_NO_ACTION; /* field not accessible from this direction */
11928 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11929 return MP_NO_ACTION;
11931 if (IS_EM_GATE(element))
11933 if (!player->key[EM_GATE_NR(element)])
11934 return MP_NO_ACTION;
11936 else if (IS_EM_GATE_GRAY(element))
11938 if (!player->key[EM_GATE_GRAY_NR(element)])
11939 return MP_NO_ACTION;
11941 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11943 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11944 return MP_NO_ACTION;
11946 else if (IS_EMC_GATE(element))
11948 if (!player->key[EMC_GATE_NR(element)])
11949 return MP_NO_ACTION;
11951 else if (IS_EMC_GATE_GRAY(element))
11953 if (!player->key[EMC_GATE_GRAY_NR(element)])
11954 return MP_NO_ACTION;
11956 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11958 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11959 return MP_NO_ACTION;
11961 else if (IS_SP_PORT(element))
11963 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11964 element == EL_SP_GRAVITY_PORT_RIGHT ||
11965 element == EL_SP_GRAVITY_PORT_UP ||
11966 element == EL_SP_GRAVITY_PORT_DOWN)
11967 #if USE_PLAYER_GRAVITY
11968 player->gravity = !player->gravity;
11970 game.gravity = !game.gravity;
11972 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11973 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11974 element == EL_SP_GRAVITY_ON_PORT_UP ||
11975 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11976 #if USE_PLAYER_GRAVITY
11977 player->gravity = TRUE;
11979 game.gravity = TRUE;
11981 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11982 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11983 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11984 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11985 #if USE_PLAYER_GRAVITY
11986 player->gravity = FALSE;
11988 game.gravity = FALSE;
11992 /* automatically move to the next field with double speed */
11993 player->programmed_action = move_direction;
11995 if (player->move_delay_reset_counter == 0)
11997 player->move_delay_reset_counter = 2; /* two double speed steps */
11999 DOUBLE_PLAYER_SPEED(player);
12002 PlayLevelSoundAction(x, y, ACTION_PASSING);
12004 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12008 if (mode != DF_SNAP)
12010 GfxElement[x][y] = GFX_ELEMENT(element);
12011 player->is_digging = TRUE;
12014 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12016 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12017 player->index_bit, dig_side);
12019 if (mode == DF_SNAP)
12021 #if USE_NEW_SNAP_DELAY
12022 if (level.block_snap_field)
12023 setFieldForSnapping(x, y, element, move_direction);
12025 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12027 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12030 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12031 player->index_bit, dig_side);
12034 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12038 if (is_player && mode != DF_SNAP)
12040 GfxElement[x][y] = element;
12041 player->is_collecting = TRUE;
12044 if (element == EL_SPEED_PILL)
12046 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12048 else if (element == EL_EXTRA_TIME && level.time > 0)
12050 TimeLeft += level.extra_time;
12051 DrawGameValue_Time(TimeLeft);
12053 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12055 player->shield_normal_time_left += level.shield_normal_time;
12056 if (element == EL_SHIELD_DEADLY)
12057 player->shield_deadly_time_left += level.shield_deadly_time;
12059 else if (element == EL_DYNAMITE ||
12060 element == EL_EM_DYNAMITE ||
12061 element == EL_SP_DISK_RED)
12063 if (player->inventory_size < MAX_INVENTORY_SIZE)
12064 player->inventory_element[player->inventory_size++] = element;
12067 DrawGameDoorValues();
12069 DrawGameValue_Dynamite(local_player->inventory_size);
12072 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12074 player->dynabomb_count++;
12075 player->dynabombs_left++;
12077 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12079 player->dynabomb_size++;
12081 else if (element == EL_DYNABOMB_INCREASE_POWER)
12083 player->dynabomb_xl = TRUE;
12085 else if (IS_KEY(element))
12087 player->key[KEY_NR(element)] = TRUE;
12090 DrawGameDoorValues();
12092 DrawGameValue_Keys(player->key);
12095 redraw_mask |= REDRAW_DOOR_1;
12097 else if (IS_ENVELOPE(element))
12099 player->show_envelope = element;
12101 else if (element == EL_EMC_LENSES)
12103 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12105 RedrawAllInvisibleElementsForLenses();
12107 else if (element == EL_EMC_MAGNIFIER)
12109 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12111 RedrawAllInvisibleElementsForMagnifier();
12113 else if (IS_DROPPABLE(element) ||
12114 IS_THROWABLE(element)) /* can be collected and dropped */
12118 if (collect_count == 0)
12119 player->inventory_infinite_element = element;
12121 for (i = 0; i < collect_count; i++)
12122 if (player->inventory_size < MAX_INVENTORY_SIZE)
12123 player->inventory_element[player->inventory_size++] = element;
12126 DrawGameDoorValues();
12128 DrawGameValue_Dynamite(local_player->inventory_size);
12131 else if (collect_count > 0)
12133 local_player->gems_still_needed -= collect_count;
12134 if (local_player->gems_still_needed < 0)
12135 local_player->gems_still_needed = 0;
12137 DrawGameValue_Emeralds(local_player->gems_still_needed);
12140 RaiseScoreElement(element);
12141 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12144 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12145 player->index_bit, dig_side);
12147 if (mode == DF_SNAP)
12149 #if USE_NEW_SNAP_DELAY
12150 if (level.block_snap_field)
12151 setFieldForSnapping(x, y, element, move_direction);
12153 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12155 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12158 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12159 player->index_bit, dig_side);
12162 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12164 if (mode == DF_SNAP && element != EL_BD_ROCK)
12165 return MP_NO_ACTION;
12167 if (CAN_FALL(element) && dy)
12168 return MP_NO_ACTION;
12170 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12171 !(element == EL_SPRING && level.use_spring_bug))
12172 return MP_NO_ACTION;
12174 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12175 ((move_direction & MV_VERTICAL &&
12176 ((element_info[element].move_pattern & MV_LEFT &&
12177 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12178 (element_info[element].move_pattern & MV_RIGHT &&
12179 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12180 (move_direction & MV_HORIZONTAL &&
12181 ((element_info[element].move_pattern & MV_UP &&
12182 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12183 (element_info[element].move_pattern & MV_DOWN &&
12184 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12185 return MP_NO_ACTION;
12187 /* do not push elements already moving away faster than player */
12188 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12189 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12190 return MP_NO_ACTION;
12192 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12194 if (player->push_delay_value == -1 || !player_was_pushing)
12195 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12197 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12199 if (player->push_delay_value == -1)
12200 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12202 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12204 if (!player->is_pushing)
12205 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12208 player->is_pushing = TRUE;
12209 player->is_active = TRUE;
12211 if (!(IN_LEV_FIELD(nextx, nexty) &&
12212 (IS_FREE(nextx, nexty) ||
12213 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12214 IS_SB_ELEMENT(element)))))
12215 return MP_NO_ACTION;
12217 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12218 return MP_NO_ACTION;
12220 if (player->push_delay == -1) /* new pushing; restart delay */
12221 player->push_delay = 0;
12223 if (player->push_delay < player->push_delay_value &&
12224 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12225 element != EL_SPRING && element != EL_BALLOON)
12227 /* make sure that there is no move delay before next try to push */
12228 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12229 player->move_delay = 0;
12231 return MP_NO_ACTION;
12234 if (IS_SB_ELEMENT(element))
12236 if (element == EL_SOKOBAN_FIELD_FULL)
12238 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12239 local_player->sokobanfields_still_needed++;
12242 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12244 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12245 local_player->sokobanfields_still_needed--;
12248 Feld[x][y] = EL_SOKOBAN_OBJECT;
12250 if (Back[x][y] == Back[nextx][nexty])
12251 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12252 else if (Back[x][y] != 0)
12253 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12256 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12259 if (local_player->sokobanfields_still_needed == 0 &&
12260 game.emulation == EMU_SOKOBAN)
12262 player->LevelSolved = player->GameOver = TRUE;
12263 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12267 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12269 InitMovingField(x, y, move_direction);
12270 GfxAction[x][y] = ACTION_PUSHING;
12272 if (mode == DF_SNAP)
12273 ContinueMoving(x, y);
12275 MovPos[x][y] = (dx != 0 ? dx : dy);
12277 Pushed[x][y] = TRUE;
12278 Pushed[nextx][nexty] = TRUE;
12280 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12281 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12283 player->push_delay_value = -1; /* get new value later */
12285 /* check for element change _after_ element has been pushed */
12286 if (game.use_change_when_pushing_bug)
12288 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12289 player->index_bit, dig_side);
12290 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12291 player->index_bit, dig_side);
12294 else if (IS_SWITCHABLE(element))
12296 if (PLAYER_SWITCHING(player, x, y))
12298 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12299 player->index_bit, dig_side);
12304 player->is_switching = TRUE;
12305 player->switch_x = x;
12306 player->switch_y = y;
12308 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12310 if (element == EL_ROBOT_WHEEL)
12312 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12316 DrawLevelField(x, y);
12318 else if (element == EL_SP_TERMINAL)
12323 SCAN_PLAYFIELD(xx, yy)
12325 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12328 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12330 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12331 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12334 else if (IS_BELT_SWITCH(element))
12336 ToggleBeltSwitch(x, y);
12338 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12339 element == EL_SWITCHGATE_SWITCH_DOWN)
12341 ToggleSwitchgateSwitch(x, y);
12343 else if (element == EL_LIGHT_SWITCH ||
12344 element == EL_LIGHT_SWITCH_ACTIVE)
12346 ToggleLightSwitch(x, y);
12348 else if (element == EL_TIMEGATE_SWITCH)
12350 ActivateTimegateSwitch(x, y);
12352 else if (element == EL_BALLOON_SWITCH_LEFT ||
12353 element == EL_BALLOON_SWITCH_RIGHT ||
12354 element == EL_BALLOON_SWITCH_UP ||
12355 element == EL_BALLOON_SWITCH_DOWN ||
12356 element == EL_BALLOON_SWITCH_NONE ||
12357 element == EL_BALLOON_SWITCH_ANY)
12359 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12360 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12361 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12362 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12363 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12366 else if (element == EL_LAMP)
12368 Feld[x][y] = EL_LAMP_ACTIVE;
12369 local_player->lights_still_needed--;
12371 ResetGfxAnimation(x, y);
12372 DrawLevelField(x, y);
12374 else if (element == EL_TIME_ORB_FULL)
12376 Feld[x][y] = EL_TIME_ORB_EMPTY;
12378 if (level.time > 0 || level.use_time_orb_bug)
12380 TimeLeft += level.time_orb_time;
12381 DrawGameValue_Time(TimeLeft);
12384 ResetGfxAnimation(x, y);
12385 DrawLevelField(x, y);
12387 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12388 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12392 game.ball_state = !game.ball_state;
12395 SCAN_PLAYFIELD(xx, yy)
12397 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12400 int e = Feld[xx][yy];
12402 if (game.ball_state)
12404 if (e == EL_EMC_MAGIC_BALL)
12405 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12406 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12407 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12411 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12412 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12413 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12414 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12419 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12420 player->index_bit, dig_side);
12422 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12423 player->index_bit, dig_side);
12425 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12426 player->index_bit, dig_side);
12432 if (!PLAYER_SWITCHING(player, x, y))
12434 player->is_switching = TRUE;
12435 player->switch_x = x;
12436 player->switch_y = y;
12438 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12439 player->index_bit, dig_side);
12440 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12441 player->index_bit, dig_side);
12443 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12444 player->index_bit, dig_side);
12445 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12446 player->index_bit, dig_side);
12449 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12450 player->index_bit, dig_side);
12451 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12452 player->index_bit, dig_side);
12454 return MP_NO_ACTION;
12457 player->push_delay = -1;
12459 if (is_player) /* function can also be called by EL_PENGUIN */
12461 if (Feld[x][y] != element) /* really digged/collected something */
12463 player->is_collecting = !player->is_digging;
12464 player->is_active = TRUE;
12471 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12473 int jx = player->jx, jy = player->jy;
12474 int x = jx + dx, y = jy + dy;
12475 int snap_direction = (dx == -1 ? MV_LEFT :
12476 dx == +1 ? MV_RIGHT :
12478 dy == +1 ? MV_DOWN : MV_NONE);
12479 boolean can_continue_snapping = (level.continuous_snapping &&
12480 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12482 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12485 if (!player->active || !IN_LEV_FIELD(x, y))
12493 if (player->MovPos == 0)
12494 player->is_pushing = FALSE;
12496 player->is_snapping = FALSE;
12498 if (player->MovPos == 0)
12500 player->is_moving = FALSE;
12501 player->is_digging = FALSE;
12502 player->is_collecting = FALSE;
12508 #if USE_NEW_CONTINUOUS_SNAPPING
12509 /* prevent snapping with already pressed snap key when not allowed */
12510 if (player->is_snapping && !can_continue_snapping)
12513 if (player->is_snapping)
12517 player->MovDir = snap_direction;
12519 if (player->MovPos == 0)
12521 player->is_moving = FALSE;
12522 player->is_digging = FALSE;
12523 player->is_collecting = FALSE;
12526 player->is_dropping = FALSE;
12527 player->is_dropping_pressed = FALSE;
12528 player->drop_pressed_delay = 0;
12530 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12533 player->is_snapping = TRUE;
12534 player->is_active = TRUE;
12536 if (player->MovPos == 0)
12538 player->is_moving = FALSE;
12539 player->is_digging = FALSE;
12540 player->is_collecting = FALSE;
12543 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12544 DrawLevelField(player->last_jx, player->last_jy);
12546 DrawLevelField(x, y);
12551 boolean DropElement(struct PlayerInfo *player)
12553 int old_element, new_element;
12554 int dropx = player->jx, dropy = player->jy;
12555 int drop_direction = player->MovDir;
12556 int drop_side = drop_direction;
12557 int drop_element = (player->inventory_size > 0 ?
12558 player->inventory_element[player->inventory_size - 1] :
12559 player->inventory_infinite_element != EL_UNDEFINED ?
12560 player->inventory_infinite_element :
12561 player->dynabombs_left > 0 ?
12562 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12565 player->is_dropping_pressed = TRUE;
12567 /* do not drop an element on top of another element; when holding drop key
12568 pressed without moving, dropped element must move away before the next
12569 element can be dropped (this is especially important if the next element
12570 is dynamite, which can be placed on background for historical reasons) */
12571 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12574 if (IS_THROWABLE(drop_element))
12576 dropx += GET_DX_FROM_DIR(drop_direction);
12577 dropy += GET_DY_FROM_DIR(drop_direction);
12579 if (!IN_LEV_FIELD(dropx, dropy))
12583 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12584 new_element = drop_element; /* default: no change when dropping */
12586 /* check if player is active, not moving and ready to drop */
12587 if (!player->active || player->MovPos || player->drop_delay > 0)
12590 /* check if player has anything that can be dropped */
12591 if (new_element == EL_UNDEFINED)
12594 /* check if drop key was pressed long enough for EM style dynamite */
12595 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12598 /* check if anything can be dropped at the current position */
12599 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12602 /* collected custom elements can only be dropped on empty fields */
12603 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12606 if (old_element != EL_EMPTY)
12607 Back[dropx][dropy] = old_element; /* store old element on this field */
12609 ResetGfxAnimation(dropx, dropy);
12610 ResetRandomAnimationValue(dropx, dropy);
12612 if (player->inventory_size > 0 ||
12613 player->inventory_infinite_element != EL_UNDEFINED)
12615 if (player->inventory_size > 0)
12617 player->inventory_size--;
12620 DrawGameDoorValues();
12622 DrawGameValue_Dynamite(local_player->inventory_size);
12625 if (new_element == EL_DYNAMITE)
12626 new_element = EL_DYNAMITE_ACTIVE;
12627 else if (new_element == EL_EM_DYNAMITE)
12628 new_element = EL_EM_DYNAMITE_ACTIVE;
12629 else if (new_element == EL_SP_DISK_RED)
12630 new_element = EL_SP_DISK_RED_ACTIVE;
12633 Feld[dropx][dropy] = new_element;
12635 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12636 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12637 el2img(Feld[dropx][dropy]), 0);
12639 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12641 /* needed if previous element just changed to "empty" in the last frame */
12642 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12644 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12645 player->index_bit, drop_side);
12646 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12648 player->index_bit, drop_side);
12650 TestIfElementTouchesCustomElement(dropx, dropy);
12652 else /* player is dropping a dyna bomb */
12654 player->dynabombs_left--;
12656 Feld[dropx][dropy] = new_element;
12658 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12659 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12660 el2img(Feld[dropx][dropy]), 0);
12662 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12665 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12666 InitField_WithBug1(dropx, dropy, FALSE);
12668 new_element = Feld[dropx][dropy]; /* element might have changed */
12670 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12671 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12673 int move_direction, nextx, nexty;
12675 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12676 MovDir[dropx][dropy] = drop_direction;
12678 move_direction = MovDir[dropx][dropy];
12679 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12680 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12682 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12683 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12686 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12687 player->is_dropping = TRUE;
12689 player->drop_pressed_delay = 0;
12690 player->is_dropping_pressed = FALSE;
12692 player->drop_x = dropx;
12693 player->drop_y = dropy;
12698 /* ------------------------------------------------------------------------- */
12699 /* game sound playing functions */
12700 /* ------------------------------------------------------------------------- */
12702 static int *loop_sound_frame = NULL;
12703 static int *loop_sound_volume = NULL;
12705 void InitPlayLevelSound()
12707 int num_sounds = getSoundListSize();
12709 checked_free(loop_sound_frame);
12710 checked_free(loop_sound_volume);
12712 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12713 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12716 static void PlayLevelSound(int x, int y, int nr)
12718 int sx = SCREENX(x), sy = SCREENY(y);
12719 int volume, stereo_position;
12720 int max_distance = 8;
12721 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12723 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12724 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12727 if (!IN_LEV_FIELD(x, y) ||
12728 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12729 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12732 volume = SOUND_MAX_VOLUME;
12734 if (!IN_SCR_FIELD(sx, sy))
12736 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12737 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12739 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12742 stereo_position = (SOUND_MAX_LEFT +
12743 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12744 (SCR_FIELDX + 2 * max_distance));
12746 if (IS_LOOP_SOUND(nr))
12748 /* This assures that quieter loop sounds do not overwrite louder ones,
12749 while restarting sound volume comparison with each new game frame. */
12751 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12754 loop_sound_volume[nr] = volume;
12755 loop_sound_frame[nr] = FrameCounter;
12758 PlaySoundExt(nr, volume, stereo_position, type);
12761 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12763 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12764 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12765 y < LEVELY(BY1) ? LEVELY(BY1) :
12766 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12770 static void PlayLevelSoundAction(int x, int y, int action)
12772 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12775 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12777 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12779 if (sound_effect != SND_UNDEFINED)
12780 PlayLevelSound(x, y, sound_effect);
12783 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12786 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12788 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12789 PlayLevelSound(x, y, sound_effect);
12792 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12794 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12796 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12797 PlayLevelSound(x, y, sound_effect);
12800 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12802 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12804 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12805 StopSound(sound_effect);
12808 static void PlayLevelMusic()
12810 if (levelset.music[level_nr] != MUS_UNDEFINED)
12811 PlayMusic(levelset.music[level_nr]); /* from config file */
12813 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12816 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12818 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12823 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12827 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12831 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12835 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12839 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12843 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12847 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12850 case SAMPLE_android_clone:
12851 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12854 case SAMPLE_android_move:
12855 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12858 case SAMPLE_spring:
12859 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12863 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12867 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12870 case SAMPLE_eater_eat:
12871 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12875 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12878 case SAMPLE_collect:
12879 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12882 case SAMPLE_diamond:
12883 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12886 case SAMPLE_squash:
12887 /* !!! CHECK THIS !!! */
12889 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12891 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12895 case SAMPLE_wonderfall:
12896 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12900 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12904 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12908 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12912 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12916 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12920 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12923 case SAMPLE_wonder:
12924 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12928 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12931 case SAMPLE_exit_open:
12932 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12935 case SAMPLE_exit_leave:
12936 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12939 case SAMPLE_dynamite:
12940 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12944 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12948 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12952 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12956 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12960 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12964 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12968 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12973 void RaiseScore(int value)
12975 local_player->score += value;
12977 DrawGameValue_Score(local_player->score);
12980 void RaiseScoreElement(int element)
12985 case EL_BD_DIAMOND:
12986 case EL_EMERALD_YELLOW:
12987 case EL_EMERALD_RED:
12988 case EL_EMERALD_PURPLE:
12989 case EL_SP_INFOTRON:
12990 RaiseScore(level.score[SC_EMERALD]);
12993 RaiseScore(level.score[SC_DIAMOND]);
12996 RaiseScore(level.score[SC_CRYSTAL]);
12999 RaiseScore(level.score[SC_PEARL]);
13002 case EL_BD_BUTTERFLY:
13003 case EL_SP_ELECTRON:
13004 RaiseScore(level.score[SC_BUG]);
13007 case EL_BD_FIREFLY:
13008 case EL_SP_SNIKSNAK:
13009 RaiseScore(level.score[SC_SPACESHIP]);
13012 case EL_DARK_YAMYAM:
13013 RaiseScore(level.score[SC_YAMYAM]);
13016 RaiseScore(level.score[SC_ROBOT]);
13019 RaiseScore(level.score[SC_PACMAN]);
13022 RaiseScore(level.score[SC_NUT]);
13025 case EL_EM_DYNAMITE:
13026 case EL_SP_DISK_RED:
13027 case EL_DYNABOMB_INCREASE_NUMBER:
13028 case EL_DYNABOMB_INCREASE_SIZE:
13029 case EL_DYNABOMB_INCREASE_POWER:
13030 RaiseScore(level.score[SC_DYNAMITE]);
13032 case EL_SHIELD_NORMAL:
13033 case EL_SHIELD_DEADLY:
13034 RaiseScore(level.score[SC_SHIELD]);
13036 case EL_EXTRA_TIME:
13037 RaiseScore(level.extra_time_score);
13051 RaiseScore(level.score[SC_KEY]);
13054 RaiseScore(element_info[element].collect_score);
13059 void RequestQuitGame(boolean ask_if_really_quit)
13061 if (AllPlayersGone ||
13062 !ask_if_really_quit ||
13063 level_editor_test_game ||
13064 Request("Do you really want to quit the game ?",
13065 REQ_ASK | REQ_STAY_CLOSED))
13067 #if defined(NETWORK_AVALIABLE)
13068 if (options.network)
13069 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13073 game_status = GAME_MODE_MAIN;
13079 if (tape.playing && tape.deactivate_display)
13080 TapeDeactivateDisplayOff(TRUE);
13082 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13084 if (tape.playing && tape.deactivate_display)
13085 TapeDeactivateDisplayOn();
13090 /* ---------- new game button stuff ---------------------------------------- */
13092 /* graphic position values for game buttons */
13093 #define GAME_BUTTON_XSIZE 30
13094 #define GAME_BUTTON_YSIZE 30
13095 #define GAME_BUTTON_XPOS 5
13096 #define GAME_BUTTON_YPOS 215
13097 #define SOUND_BUTTON_XPOS 5
13098 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13100 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13101 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13102 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13103 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13104 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13105 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13112 } gamebutton_info[NUM_GAME_BUTTONS] =
13115 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13120 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13121 GAME_CTRL_ID_PAUSE,
13125 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13130 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13131 SOUND_CTRL_ID_MUSIC,
13132 "background music on/off"
13135 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13136 SOUND_CTRL_ID_LOOPS,
13137 "sound loops on/off"
13140 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13141 SOUND_CTRL_ID_SIMPLE,
13142 "normal sounds on/off"
13146 void CreateGameButtons()
13150 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13152 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13153 struct GadgetInfo *gi;
13156 unsigned long event_mask;
13157 int gd_xoffset, gd_yoffset;
13158 int gd_x1, gd_x2, gd_y1, gd_y2;
13161 gd_xoffset = gamebutton_info[i].x;
13162 gd_yoffset = gamebutton_info[i].y;
13163 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13164 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13166 if (id == GAME_CTRL_ID_STOP ||
13167 id == GAME_CTRL_ID_PAUSE ||
13168 id == GAME_CTRL_ID_PLAY)
13170 button_type = GD_TYPE_NORMAL_BUTTON;
13172 event_mask = GD_EVENT_RELEASED;
13173 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13174 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13178 button_type = GD_TYPE_CHECK_BUTTON;
13180 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13181 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13182 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13183 event_mask = GD_EVENT_PRESSED;
13184 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13185 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13188 gi = CreateGadget(GDI_CUSTOM_ID, id,
13189 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13190 GDI_X, DX + gd_xoffset,
13191 GDI_Y, DY + gd_yoffset,
13192 GDI_WIDTH, GAME_BUTTON_XSIZE,
13193 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13194 GDI_TYPE, button_type,
13195 GDI_STATE, GD_BUTTON_UNPRESSED,
13196 GDI_CHECKED, checked,
13197 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13198 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13199 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13200 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13201 GDI_EVENT_MASK, event_mask,
13202 GDI_CALLBACK_ACTION, HandleGameButtons,
13206 Error(ERR_EXIT, "cannot create gadget");
13208 game_gadget[id] = gi;
13212 void FreeGameButtons()
13216 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13217 FreeGadget(game_gadget[i]);
13220 static void MapGameButtons()
13224 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13225 MapGadget(game_gadget[i]);
13228 void UnmapGameButtons()
13232 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13233 UnmapGadget(game_gadget[i]);
13236 static void HandleGameButtons(struct GadgetInfo *gi)
13238 int id = gi->custom_id;
13240 if (game_status != GAME_MODE_PLAYING)
13245 case GAME_CTRL_ID_STOP:
13249 RequestQuitGame(TRUE);
13252 case GAME_CTRL_ID_PAUSE:
13253 if (options.network)
13255 #if defined(NETWORK_AVALIABLE)
13257 SendToServer_ContinuePlaying();
13259 SendToServer_PausePlaying();
13263 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13266 case GAME_CTRL_ID_PLAY:
13269 #if defined(NETWORK_AVALIABLE)
13270 if (options.network)
13271 SendToServer_ContinuePlaying();
13275 tape.pausing = FALSE;
13276 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13281 case SOUND_CTRL_ID_MUSIC:
13282 if (setup.sound_music)
13284 setup.sound_music = FALSE;
13287 else if (audio.music_available)
13289 setup.sound = setup.sound_music = TRUE;
13291 SetAudioMode(setup.sound);
13297 case SOUND_CTRL_ID_LOOPS:
13298 if (setup.sound_loops)
13299 setup.sound_loops = FALSE;
13300 else if (audio.loops_available)
13302 setup.sound = setup.sound_loops = TRUE;
13303 SetAudioMode(setup.sound);
13307 case SOUND_CTRL_ID_SIMPLE:
13308 if (setup.sound_simple)
13309 setup.sound_simple = FALSE;
13310 else if (audio.sound_available)
13312 setup.sound = setup.sound_simple = TRUE;
13313 SetAudioMode(setup.sound);