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)
8426 printf("::: CE_SCORE_GETS_ZERO\n");
8429 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8430 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8433 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8438 This is a very special case that seems to be a mixture between
8439 CheckElementChange() and CheckTriggeredElementChange(): while
8440 the first one only affects single elements that are triggered
8441 directly, the second one affects multiple elements in the playfield
8442 that are triggered indirectly by another element. This is a third
8443 case: Changing the CE score always affects multiple identical CEs,
8444 so every affected CE must be checked, not only the single CE for
8445 which the CE score was changed in the first place (as every instance
8446 of that CE shares the same CE score, and therefore also can change)!
8448 SCAN_PLAYFIELD(xx, yy)
8450 if (Feld[xx][yy] == element)
8451 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8452 CE_SCORE_GETS_ZERO);
8463 /* ---------- engine actions ------------------------------------------ */
8465 case CA_SET_ENGINE_SCAN_MODE:
8467 InitPlayfieldScanMode(action_arg);
8477 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8479 int old_element = Feld[x][y];
8480 int new_element = get_element_from_group_element(element);
8481 int previous_move_direction = MovDir[x][y];
8482 #if USE_NEW_CUSTOM_VALUE
8483 int last_ce_value = CustomValue[x][y];
8485 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8486 boolean add_player_onto_element = (new_element_is_player &&
8487 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8488 IS_WALKABLE(old_element));
8491 /* check if element under the player changes from accessible to unaccessible
8492 (needed for special case of dropping element which then changes) */
8493 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8494 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8502 if (!add_player_onto_element)
8504 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8505 RemoveMovingField(x, y);
8509 Feld[x][y] = new_element;
8511 #if !USE_GFX_RESET_GFX_ANIMATION
8512 ResetGfxAnimation(x, y);
8513 ResetRandomAnimationValue(x, y);
8516 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8517 MovDir[x][y] = previous_move_direction;
8519 #if USE_NEW_CUSTOM_VALUE
8520 if (element_info[new_element].use_last_ce_value)
8521 CustomValue[x][y] = last_ce_value;
8524 InitField_WithBug1(x, y, FALSE);
8526 new_element = Feld[x][y]; /* element may have changed */
8528 #if USE_GFX_RESET_GFX_ANIMATION
8529 ResetGfxAnimation(x, y);
8530 ResetRandomAnimationValue(x, y);
8533 DrawLevelField(x, y);
8535 if (GFX_CRUMBLED(new_element))
8536 DrawLevelFieldCrumbledSandNeighbours(x, y);
8540 /* check if element under the player changes from accessible to unaccessible
8541 (needed for special case of dropping element which then changes) */
8542 /* (must be checked after creating new element for walkable group elements) */
8543 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8544 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8552 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8553 if (new_element_is_player)
8554 RelocatePlayer(x, y, new_element);
8557 ChangeCount[x][y]++; /* count number of changes in the same frame */
8559 TestIfBadThingTouchesPlayer(x, y);
8560 TestIfPlayerTouchesCustomElement(x, y);
8561 TestIfElementTouchesCustomElement(x, y);
8564 static void CreateField(int x, int y, int element)
8566 CreateFieldExt(x, y, element, FALSE);
8569 static void CreateElementFromChange(int x, int y, int element)
8571 element = GET_VALID_RUNTIME_ELEMENT(element);
8573 #if USE_STOP_CHANGED_ELEMENTS
8574 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8576 int old_element = Feld[x][y];
8578 /* prevent changed element from moving in same engine frame
8579 unless both old and new element can either fall or move */
8580 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8581 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8586 CreateFieldExt(x, y, element, TRUE);
8589 static boolean ChangeElement(int x, int y, int element, int page)
8591 struct ElementInfo *ei = &element_info[element];
8592 struct ElementChangeInfo *change = &ei->change_page[page];
8593 int ce_value = CustomValue[x][y];
8594 int ce_score = ei->collect_score;
8596 int old_element = Feld[x][y];
8598 /* always use default change event to prevent running into a loop */
8599 if (ChangeEvent[x][y] == -1)
8600 ChangeEvent[x][y] = CE_DELAY;
8602 if (ChangeEvent[x][y] == CE_DELAY)
8604 /* reset actual trigger element, trigger player and action element */
8605 change->actual_trigger_element = EL_EMPTY;
8606 change->actual_trigger_player = EL_PLAYER_1;
8607 change->actual_trigger_side = CH_SIDE_NONE;
8608 change->actual_trigger_ce_value = 0;
8609 change->actual_trigger_ce_score = 0;
8612 /* do not change elements more than a specified maximum number of changes */
8613 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8616 ChangeCount[x][y]++; /* count number of changes in the same frame */
8618 if (change->explode)
8625 if (change->use_target_content)
8627 boolean complete_replace = TRUE;
8628 boolean can_replace[3][3];
8631 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8634 boolean is_walkable;
8635 boolean is_diggable;
8636 boolean is_collectible;
8637 boolean is_removable;
8638 boolean is_destructible;
8639 int ex = x + xx - 1;
8640 int ey = y + yy - 1;
8641 int content_element = change->target_content.e[xx][yy];
8644 can_replace[xx][yy] = TRUE;
8646 if (ex == x && ey == y) /* do not check changing element itself */
8649 if (content_element == EL_EMPTY_SPACE)
8651 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8656 if (!IN_LEV_FIELD(ex, ey))
8658 can_replace[xx][yy] = FALSE;
8659 complete_replace = FALSE;
8666 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8667 e = MovingOrBlocked2Element(ex, ey);
8669 is_empty = (IS_FREE(ex, ey) ||
8670 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8672 is_walkable = (is_empty || IS_WALKABLE(e));
8673 is_diggable = (is_empty || IS_DIGGABLE(e));
8674 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8675 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8676 is_removable = (is_diggable || is_collectible);
8678 can_replace[xx][yy] =
8679 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8680 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8681 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8682 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8683 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8684 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8685 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8687 if (!can_replace[xx][yy])
8688 complete_replace = FALSE;
8691 if (!change->only_if_complete || complete_replace)
8693 boolean something_has_changed = FALSE;
8695 if (change->only_if_complete && change->use_random_replace &&
8696 RND(100) < change->random_percentage)
8699 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8701 int ex = x + xx - 1;
8702 int ey = y + yy - 1;
8703 int content_element;
8705 if (can_replace[xx][yy] && (!change->use_random_replace ||
8706 RND(100) < change->random_percentage))
8708 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8709 RemoveMovingField(ex, ey);
8711 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8713 content_element = change->target_content.e[xx][yy];
8714 target_element = GET_TARGET_ELEMENT(content_element, change,
8715 ce_value, ce_score);
8717 CreateElementFromChange(ex, ey, target_element);
8719 something_has_changed = TRUE;
8721 /* for symmetry reasons, freeze newly created border elements */
8722 if (ex != x || ey != y)
8723 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8727 if (something_has_changed)
8729 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8730 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8736 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8737 ce_value, ce_score);
8739 if (element == EL_DIAGONAL_GROWING ||
8740 element == EL_DIAGONAL_SHRINKING)
8742 target_element = Store[x][y];
8744 Store[x][y] = EL_EMPTY;
8747 CreateElementFromChange(x, y, target_element);
8749 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8750 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8753 /* this uses direct change before indirect change */
8754 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8759 #if USE_NEW_DELAYED_ACTION
8761 static void HandleElementChange(int x, int y, int page)
8763 int element = MovingOrBlocked2Element(x, y);
8764 struct ElementInfo *ei = &element_info[element];
8765 struct ElementChangeInfo *change = &ei->change_page[page];
8768 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8769 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8772 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8773 x, y, element, element_info[element].token_name);
8774 printf("HandleElementChange(): This should never happen!\n");
8779 /* this can happen with classic bombs on walkable, changing elements */
8780 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8783 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8784 ChangeDelay[x][y] = 0;
8790 if (ChangeDelay[x][y] == 0) /* initialize element change */
8792 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8794 if (change->can_change)
8796 ResetGfxAnimation(x, y);
8797 ResetRandomAnimationValue(x, y);
8799 if (change->pre_change_function)
8800 change->pre_change_function(x, y);
8804 ChangeDelay[x][y]--;
8806 if (ChangeDelay[x][y] != 0) /* continue element change */
8808 if (change->can_change)
8810 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8812 if (IS_ANIMATED(graphic))
8813 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8815 if (change->change_function)
8816 change->change_function(x, y);
8819 else /* finish element change */
8821 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8823 page = ChangePage[x][y];
8824 ChangePage[x][y] = -1;
8826 change = &ei->change_page[page];
8829 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8831 ChangeDelay[x][y] = 1; /* try change after next move step */
8832 ChangePage[x][y] = page; /* remember page to use for change */
8837 if (change->can_change)
8839 if (ChangeElement(x, y, element, page))
8841 if (change->post_change_function)
8842 change->post_change_function(x, y);
8846 if (change->has_action)
8847 ExecuteCustomElementAction(x, y, element, page);
8853 static void HandleElementChange(int x, int y, int page)
8855 int element = MovingOrBlocked2Element(x, y);
8856 struct ElementInfo *ei = &element_info[element];
8857 struct ElementChangeInfo *change = &ei->change_page[page];
8860 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8863 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8864 x, y, element, element_info[element].token_name);
8865 printf("HandleElementChange(): This should never happen!\n");
8870 /* this can happen with classic bombs on walkable, changing elements */
8871 if (!CAN_CHANGE(element))
8874 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8875 ChangeDelay[x][y] = 0;
8881 if (ChangeDelay[x][y] == 0) /* initialize element change */
8883 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8885 ResetGfxAnimation(x, y);
8886 ResetRandomAnimationValue(x, y);
8888 if (change->pre_change_function)
8889 change->pre_change_function(x, y);
8892 ChangeDelay[x][y]--;
8894 if (ChangeDelay[x][y] != 0) /* continue element change */
8896 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8898 if (IS_ANIMATED(graphic))
8899 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8901 if (change->change_function)
8902 change->change_function(x, y);
8904 else /* finish element change */
8906 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8908 page = ChangePage[x][y];
8909 ChangePage[x][y] = -1;
8911 change = &ei->change_page[page];
8914 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8916 ChangeDelay[x][y] = 1; /* try change after next move step */
8917 ChangePage[x][y] = page; /* remember page to use for change */
8922 if (ChangeElement(x, y, element, page))
8924 if (change->post_change_function)
8925 change->post_change_function(x, y);
8932 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8933 int trigger_element,
8939 boolean change_done_any = FALSE;
8940 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8943 if (!(trigger_events[trigger_element][trigger_event]))
8946 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8948 int element = EL_CUSTOM_START + i;
8949 boolean change_done = FALSE;
8952 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8953 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8956 for (p = 0; p < element_info[element].num_change_pages; p++)
8958 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8960 if (change->can_change_or_has_action &&
8961 change->has_event[trigger_event] &&
8962 change->trigger_side & trigger_side &&
8963 change->trigger_player & trigger_player &&
8964 change->trigger_page & trigger_page_bits &&
8965 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8967 change->actual_trigger_element = trigger_element;
8968 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8969 change->actual_trigger_side = trigger_side;
8970 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8971 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8973 if ((change->can_change && !change_done) || change->has_action)
8978 SCAN_PLAYFIELD(x, y)
8980 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8983 if (Feld[x][y] == element)
8985 if (change->can_change && !change_done)
8987 ChangeDelay[x][y] = 1;
8988 ChangeEvent[x][y] = trigger_event;
8990 HandleElementChange(x, y, p);
8992 #if USE_NEW_DELAYED_ACTION
8993 else if (change->has_action)
8995 ExecuteCustomElementAction(x, y, element, p);
8996 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8999 if (change->has_action)
9001 ExecuteCustomElementAction(x, y, element, p);
9002 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9008 if (change->can_change)
9011 change_done_any = TRUE;
9018 return change_done_any;
9021 static boolean CheckElementChangeExt(int x, int y,
9023 int trigger_element,
9028 boolean change_done = FALSE;
9031 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9032 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9035 if (Feld[x][y] == EL_BLOCKED)
9037 Blocked2Moving(x, y, &x, &y);
9038 element = Feld[x][y];
9042 /* check if element has already changed */
9043 if (Feld[x][y] != element)
9046 /* check if element has already changed or is about to change after moving */
9047 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9048 Feld[x][y] != element) ||
9050 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9051 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9052 ChangePage[x][y] != -1)))
9056 for (p = 0; p < element_info[element].num_change_pages; p++)
9058 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9060 boolean check_trigger_element =
9061 (trigger_event == CE_TOUCHING_X ||
9062 trigger_event == CE_HITTING_X ||
9063 trigger_event == CE_HIT_BY_X);
9065 if (change->can_change_or_has_action &&
9066 change->has_event[trigger_event] &&
9067 change->trigger_side & trigger_side &&
9068 change->trigger_player & trigger_player &&
9069 (!check_trigger_element ||
9070 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9072 change->actual_trigger_element = trigger_element;
9073 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9074 change->actual_trigger_side = trigger_side;
9075 change->actual_trigger_ce_value = CustomValue[x][y];
9076 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9078 /* special case: trigger element not at (x,y) position for some events */
9079 if (check_trigger_element)
9091 { 0, 0 }, { 0, 0 }, { 0, 0 },
9095 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9096 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9098 change->actual_trigger_ce_value = CustomValue[xx][yy];
9099 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9102 if (change->can_change && !change_done)
9104 ChangeDelay[x][y] = 1;
9105 ChangeEvent[x][y] = trigger_event;
9107 HandleElementChange(x, y, p);
9111 #if USE_NEW_DELAYED_ACTION
9112 else if (change->has_action)
9114 ExecuteCustomElementAction(x, y, element, p);
9115 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9118 if (change->has_action)
9120 ExecuteCustomElementAction(x, y, element, p);
9121 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9130 static void PlayPlayerSound(struct PlayerInfo *player)
9132 int jx = player->jx, jy = player->jy;
9133 int sound_element = player->artwork_element;
9134 int last_action = player->last_action_waiting;
9135 int action = player->action_waiting;
9137 if (player->is_waiting)
9139 if (action != last_action)
9140 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9142 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9146 if (action != last_action)
9147 StopSound(element_info[sound_element].sound[last_action]);
9149 if (last_action == ACTION_SLEEPING)
9150 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9154 static void PlayAllPlayersSound()
9158 for (i = 0; i < MAX_PLAYERS; i++)
9159 if (stored_player[i].active)
9160 PlayPlayerSound(&stored_player[i]);
9163 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9165 boolean last_waiting = player->is_waiting;
9166 int move_dir = player->MovDir;
9168 player->dir_waiting = move_dir;
9169 player->last_action_waiting = player->action_waiting;
9173 if (!last_waiting) /* not waiting -> waiting */
9175 player->is_waiting = TRUE;
9177 player->frame_counter_bored =
9179 game.player_boring_delay_fixed +
9180 SimpleRND(game.player_boring_delay_random);
9181 player->frame_counter_sleeping =
9183 game.player_sleeping_delay_fixed +
9184 SimpleRND(game.player_sleeping_delay_random);
9187 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9189 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9193 if (game.player_sleeping_delay_fixed +
9194 game.player_sleeping_delay_random > 0 &&
9195 player->anim_delay_counter == 0 &&
9196 player->post_delay_counter == 0 &&
9197 FrameCounter >= player->frame_counter_sleeping)
9198 player->is_sleeping = TRUE;
9199 else if (game.player_boring_delay_fixed +
9200 game.player_boring_delay_random > 0 &&
9201 FrameCounter >= player->frame_counter_bored)
9202 player->is_bored = TRUE;
9204 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9205 player->is_bored ? ACTION_BORING :
9209 if (player->is_sleeping && player->use_murphy)
9211 /* special case for sleeping Murphy when leaning against non-free tile */
9213 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9214 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9215 !IS_MOVING(player->jx - 1, player->jy)))
9217 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9218 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9219 !IS_MOVING(player->jx + 1, player->jy)))
9220 move_dir = MV_RIGHT;
9222 player->is_sleeping = FALSE;
9224 player->dir_waiting = move_dir;
9228 if (player->is_sleeping)
9230 if (player->num_special_action_sleeping > 0)
9232 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9234 int last_special_action = player->special_action_sleeping;
9235 int num_special_action = player->num_special_action_sleeping;
9236 int special_action =
9237 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9238 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9239 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9240 last_special_action + 1 : ACTION_SLEEPING);
9241 int special_graphic =
9242 el_act_dir2img(player->artwork_element, special_action, move_dir);
9244 player->anim_delay_counter =
9245 graphic_info[special_graphic].anim_delay_fixed +
9246 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9247 player->post_delay_counter =
9248 graphic_info[special_graphic].post_delay_fixed +
9249 SimpleRND(graphic_info[special_graphic].post_delay_random);
9251 player->special_action_sleeping = special_action;
9254 if (player->anim_delay_counter > 0)
9256 player->action_waiting = player->special_action_sleeping;
9257 player->anim_delay_counter--;
9259 else if (player->post_delay_counter > 0)
9261 player->post_delay_counter--;
9265 else if (player->is_bored)
9267 if (player->num_special_action_bored > 0)
9269 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9271 int special_action =
9272 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9273 int special_graphic =
9274 el_act_dir2img(player->artwork_element, special_action, move_dir);
9276 player->anim_delay_counter =
9277 graphic_info[special_graphic].anim_delay_fixed +
9278 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9279 player->post_delay_counter =
9280 graphic_info[special_graphic].post_delay_fixed +
9281 SimpleRND(graphic_info[special_graphic].post_delay_random);
9283 player->special_action_bored = special_action;
9286 if (player->anim_delay_counter > 0)
9288 player->action_waiting = player->special_action_bored;
9289 player->anim_delay_counter--;
9291 else if (player->post_delay_counter > 0)
9293 player->post_delay_counter--;
9298 else if (last_waiting) /* waiting -> not waiting */
9300 player->is_waiting = FALSE;
9301 player->is_bored = FALSE;
9302 player->is_sleeping = FALSE;
9304 player->frame_counter_bored = -1;
9305 player->frame_counter_sleeping = -1;
9307 player->anim_delay_counter = 0;
9308 player->post_delay_counter = 0;
9310 player->dir_waiting = player->MovDir;
9311 player->action_waiting = ACTION_DEFAULT;
9313 player->special_action_bored = ACTION_DEFAULT;
9314 player->special_action_sleeping = ACTION_DEFAULT;
9318 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9320 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9321 int left = player_action & JOY_LEFT;
9322 int right = player_action & JOY_RIGHT;
9323 int up = player_action & JOY_UP;
9324 int down = player_action & JOY_DOWN;
9325 int button1 = player_action & JOY_BUTTON_1;
9326 int button2 = player_action & JOY_BUTTON_2;
9327 int dx = (left ? -1 : right ? 1 : 0);
9328 int dy = (up ? -1 : down ? 1 : 0);
9330 if (!player->active || tape.pausing)
9336 snapped = SnapField(player, dx, dy);
9340 dropped = DropElement(player);
9342 moved = MovePlayer(player, dx, dy);
9345 if (tape.single_step && tape.recording && !tape.pausing)
9347 if (button1 || (dropped && !moved))
9349 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9350 SnapField(player, 0, 0); /* stop snapping */
9354 SetPlayerWaiting(player, FALSE);
9356 return player_action;
9360 /* no actions for this player (no input at player's configured device) */
9362 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9363 SnapField(player, 0, 0);
9364 CheckGravityMovementWhenNotMoving(player);
9366 if (player->MovPos == 0)
9367 SetPlayerWaiting(player, TRUE);
9369 if (player->MovPos == 0) /* needed for tape.playing */
9370 player->is_moving = FALSE;
9372 player->is_dropping = FALSE;
9373 player->is_dropping_pressed = FALSE;
9374 player->drop_pressed_delay = 0;
9380 static void CheckLevelTime()
9384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9386 if (level.native_em_level->lev->home == 0) /* all players at home */
9388 local_player->LevelSolved = TRUE;
9389 AllPlayersGone = TRUE;
9391 level.native_em_level->lev->home = -1;
9394 if (level.native_em_level->ply[0]->alive == 0 &&
9395 level.native_em_level->ply[1]->alive == 0 &&
9396 level.native_em_level->ply[2]->alive == 0 &&
9397 level.native_em_level->ply[3]->alive == 0) /* all dead */
9398 AllPlayersGone = TRUE;
9401 if (TimeFrames >= FRAMES_PER_SECOND)
9406 for (i = 0; i < MAX_PLAYERS; i++)
9408 struct PlayerInfo *player = &stored_player[i];
9410 if (SHIELD_ON(player))
9412 player->shield_normal_time_left--;
9414 if (player->shield_deadly_time_left > 0)
9415 player->shield_deadly_time_left--;
9419 if (!level.use_step_counter)
9427 if (TimeLeft <= 10 && setup.time_limit)
9428 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9430 DrawGameValue_Time(TimeLeft);
9432 if (!TimeLeft && setup.time_limit)
9434 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9435 level.native_em_level->lev->killed_out_of_time = TRUE;
9437 for (i = 0; i < MAX_PLAYERS; i++)
9438 KillPlayer(&stored_player[i]);
9441 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9442 DrawGameValue_Time(TimePlayed);
9444 level.native_em_level->lev->time =
9445 (level.time == 0 ? TimePlayed : TimeLeft);
9448 if (tape.recording || tape.playing)
9449 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9453 void AdvanceFrameAndPlayerCounters(int player_nr)
9458 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9459 FrameCounter, FrameCounter + 1);
9462 /* advance frame counters (global frame counter and time frame counter) */
9466 /* advance player counters (counters for move delay, move animation etc.) */
9467 for (i = 0; i < MAX_PLAYERS; i++)
9469 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9470 int move_delay_value = stored_player[i].move_delay_value;
9471 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9473 if (!advance_player_counters) /* not all players may be affected */
9476 #if USE_NEW_PLAYER_ANIM
9477 if (move_frames == 0) /* less than one move per game frame */
9479 int stepsize = TILEX / move_delay_value;
9480 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9481 int count = (stored_player[i].is_moving ?
9482 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9484 if (count % delay == 0)
9489 stored_player[i].Frame += move_frames;
9491 if (stored_player[i].MovPos != 0)
9492 stored_player[i].StepFrame += move_frames;
9494 if (stored_player[i].move_delay > 0)
9495 stored_player[i].move_delay--;
9497 /* due to bugs in previous versions, counter must count up, not down */
9498 if (stored_player[i].push_delay != -1)
9499 stored_player[i].push_delay++;
9501 if (stored_player[i].drop_delay > 0)
9502 stored_player[i].drop_delay--;
9504 if (stored_player[i].is_dropping_pressed)
9505 stored_player[i].drop_pressed_delay++;
9509 void StartGameActions(boolean init_network_game, boolean record_tape,
9512 unsigned long new_random_seed = InitRND(random_seed);
9515 TapeStartRecording(new_random_seed);
9517 #if defined(NETWORK_AVALIABLE)
9518 if (init_network_game)
9520 SendToServer_StartPlaying();
9528 game_status = GAME_MODE_PLAYING;
9535 static unsigned long game_frame_delay = 0;
9536 unsigned long game_frame_delay_value;
9537 byte *recorded_player_action;
9538 byte summarized_player_action = 0;
9539 byte tape_action[MAX_PLAYERS];
9542 if (game.restart_level)
9543 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9545 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9547 if (level.native_em_level->lev->home == 0) /* all players at home */
9549 local_player->LevelSolved = TRUE;
9550 AllPlayersGone = TRUE;
9552 level.native_em_level->lev->home = -1;
9555 if (level.native_em_level->ply[0]->alive == 0 &&
9556 level.native_em_level->ply[1]->alive == 0 &&
9557 level.native_em_level->ply[2]->alive == 0 &&
9558 level.native_em_level->ply[3]->alive == 0) /* all dead */
9559 AllPlayersGone = TRUE;
9562 if (local_player->LevelSolved)
9565 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9568 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9571 game_frame_delay_value =
9572 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9574 if (tape.playing && tape.warp_forward && !tape.pausing)
9575 game_frame_delay_value = 0;
9577 /* ---------- main game synchronization point ---------- */
9579 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9581 if (network_playing && !network_player_action_received)
9583 /* try to get network player actions in time */
9585 #if defined(NETWORK_AVALIABLE)
9586 /* last chance to get network player actions without main loop delay */
9590 /* game was quit by network peer */
9591 if (game_status != GAME_MODE_PLAYING)
9594 if (!network_player_action_received)
9595 return; /* failed to get network player actions in time */
9597 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9603 /* at this point we know that we really continue executing the game */
9606 network_player_action_received = FALSE;
9609 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9611 if (tape.set_centered_player)
9613 game.centered_player_nr_next = tape.centered_player_nr_next;
9614 game.set_centered_player = TRUE;
9617 for (i = 0; i < MAX_PLAYERS; i++)
9619 summarized_player_action |= stored_player[i].action;
9621 if (!network_playing)
9622 stored_player[i].effective_action = stored_player[i].action;
9625 #if defined(NETWORK_AVALIABLE)
9626 if (network_playing)
9627 SendToServer_MovePlayer(summarized_player_action);
9630 if (!options.network && !setup.team_mode)
9631 local_player->effective_action = summarized_player_action;
9633 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9635 for (i = 0; i < MAX_PLAYERS; i++)
9636 stored_player[i].effective_action =
9637 (i == game.centered_player_nr ? summarized_player_action : 0);
9640 if (recorded_player_action != NULL)
9641 for (i = 0; i < MAX_PLAYERS; i++)
9642 stored_player[i].effective_action = recorded_player_action[i];
9644 for (i = 0; i < MAX_PLAYERS; i++)
9646 tape_action[i] = stored_player[i].effective_action;
9648 /* (this can only happen in the R'n'D game engine) */
9649 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9650 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9653 /* only record actions from input devices, but not programmed actions */
9655 TapeRecordAction(tape_action);
9657 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9659 GameActions_EM_Main();
9667 void GameActions_EM_Main()
9669 byte effective_action[MAX_PLAYERS];
9670 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9673 for (i = 0; i < MAX_PLAYERS; i++)
9674 effective_action[i] = stored_player[i].effective_action;
9677 printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
9680 GameActions_EM(effective_action, warp_mode);
9684 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9687 void GameActions_RND()
9689 int magic_wall_x = 0, magic_wall_y = 0;
9690 int i, x, y, element, graphic;
9692 InitPlayfieldScanModeVars();
9694 #if USE_ONE_MORE_CHANGE_PER_FRAME
9695 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9697 SCAN_PLAYFIELD(x, y)
9699 ChangeCount[x][y] = 0;
9700 ChangeEvent[x][y] = -1;
9706 if (game.set_centered_player)
9708 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9710 /* switching to "all players" only possible if all players fit to screen */
9711 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9713 game.centered_player_nr_next = game.centered_player_nr;
9714 game.set_centered_player = FALSE;
9717 /* do not switch focus to non-existing (or non-active) player */
9718 if (game.centered_player_nr_next >= 0 &&
9719 !stored_player[game.centered_player_nr_next].active)
9721 game.centered_player_nr_next = game.centered_player_nr;
9722 game.set_centered_player = FALSE;
9726 if (game.set_centered_player &&
9727 ScreenMovPos == 0) /* screen currently aligned at tile position */
9731 if (game.centered_player_nr_next == -1)
9733 setScreenCenteredToAllPlayers(&sx, &sy);
9737 sx = stored_player[game.centered_player_nr_next].jx;
9738 sy = stored_player[game.centered_player_nr_next].jy;
9741 game.centered_player_nr = game.centered_player_nr_next;
9742 game.set_centered_player = FALSE;
9744 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9745 DrawGameDoorValues();
9749 for (i = 0; i < MAX_PLAYERS; i++)
9751 int actual_player_action = stored_player[i].effective_action;
9754 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9755 - rnd_equinox_tetrachloride 048
9756 - rnd_equinox_tetrachloride_ii 096
9757 - rnd_emanuel_schmieg 002
9758 - doctor_sloan_ww 001, 020
9760 if (stored_player[i].MovPos == 0)
9761 CheckGravityMovement(&stored_player[i]);
9764 /* overwrite programmed action with tape action */
9765 if (stored_player[i].programmed_action)
9766 actual_player_action = stored_player[i].programmed_action;
9769 PlayerActions(&stored_player[i], actual_player_action);
9771 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9773 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9774 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9777 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9781 network_player_action_received = FALSE;
9784 ScrollScreen(NULL, SCROLL_GO_ON);
9786 /* for backwards compatibility, the following code emulates a fixed bug that
9787 occured when pushing elements (causing elements that just made their last
9788 pushing step to already (if possible) make their first falling step in the
9789 same game frame, which is bad); this code is also needed to use the famous
9790 "spring push bug" which is used in older levels and might be wanted to be
9791 used also in newer levels, but in this case the buggy pushing code is only
9792 affecting the "spring" element and no other elements */
9794 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9796 for (i = 0; i < MAX_PLAYERS; i++)
9798 struct PlayerInfo *player = &stored_player[i];
9802 if (player->active && player->is_pushing && player->is_moving &&
9804 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9805 Feld[x][y] == EL_SPRING))
9807 ContinueMoving(x, y);
9809 /* continue moving after pushing (this is actually a bug) */
9810 if (!IS_MOVING(x, y))
9819 SCAN_PLAYFIELD(x, y)
9821 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9824 ChangeCount[x][y] = 0;
9825 ChangeEvent[x][y] = -1;
9827 /* this must be handled before main playfield loop */
9828 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9831 if (MovDelay[x][y] <= 0)
9835 #if USE_NEW_SNAP_DELAY
9836 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9839 if (MovDelay[x][y] <= 0)
9842 DrawLevelField(x, y);
9844 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9850 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9852 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9853 printf("GameActions(): This should never happen!\n");
9855 ChangePage[x][y] = -1;
9860 if (WasJustMoving[x][y] > 0)
9861 WasJustMoving[x][y]--;
9862 if (WasJustFalling[x][y] > 0)
9863 WasJustFalling[x][y]--;
9864 if (CheckCollision[x][y] > 0)
9865 CheckCollision[x][y]--;
9869 /* reset finished pushing action (not done in ContinueMoving() to allow
9870 continuous pushing animation for elements with zero push delay) */
9871 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9873 ResetGfxAnimation(x, y);
9874 DrawLevelField(x, y);
9878 if (IS_BLOCKED(x, y))
9882 Blocked2Moving(x, y, &oldx, &oldy);
9883 if (!IS_MOVING(oldx, oldy))
9885 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9886 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9887 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9888 printf("GameActions(): This should never happen!\n");
9895 SCAN_PLAYFIELD(x, y)
9897 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9900 element = Feld[x][y];
9901 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9904 printf("::: %d,%d\n", x, y);
9906 if (element == EL_ROCK)
9907 printf("::: Yo man! Rocks can fall!\n");
9911 ResetGfxFrame(x, y, TRUE);
9913 if (graphic_info[graphic].anim_global_sync)
9914 GfxFrame[x][y] = FrameCounter;
9915 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9917 int old_gfx_frame = GfxFrame[x][y];
9919 GfxFrame[x][y] = CustomValue[x][y];
9922 if (GfxFrame[x][y] != old_gfx_frame)
9924 DrawLevelGraphicAnimation(x, y, graphic);
9926 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9928 int old_gfx_frame = GfxFrame[x][y];
9930 GfxFrame[x][y] = element_info[element].collect_score;
9933 if (GfxFrame[x][y] != old_gfx_frame)
9935 DrawLevelGraphicAnimation(x, y, graphic);
9937 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9939 int old_gfx_frame = GfxFrame[x][y];
9941 GfxFrame[x][y] = ChangeDelay[x][y];
9944 if (GfxFrame[x][y] != old_gfx_frame)
9946 DrawLevelGraphicAnimation(x, y, graphic);
9950 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9951 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9952 ResetRandomAnimationValue(x, y);
9954 SetRandomAnimationValue(x, y);
9956 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9958 if (IS_INACTIVE(element))
9960 if (IS_ANIMATED(graphic))
9961 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9966 /* this may take place after moving, so 'element' may have changed */
9967 if (IS_CHANGING(x, y) &&
9968 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9970 int page = element_info[element].event_page_nr[CE_DELAY];
9972 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9976 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9980 if (element == EL_CUSTOM_255)
9981 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9985 HandleElementChange(x, y, page);
9987 if (CAN_CHANGE(element))
9988 HandleElementChange(x, y, page);
9990 if (HAS_ACTION(element))
9991 ExecuteCustomElementAction(x, y, element, page);
9996 element = Feld[x][y];
9997 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10000 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10004 element = Feld[x][y];
10005 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10007 if (IS_ANIMATED(graphic) &&
10008 !IS_MOVING(x, y) &&
10010 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10012 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10013 EdelsteinFunkeln(x, y);
10015 else if ((element == EL_ACID ||
10016 element == EL_EXIT_OPEN ||
10017 element == EL_SP_EXIT_OPEN ||
10018 element == EL_SP_TERMINAL ||
10019 element == EL_SP_TERMINAL_ACTIVE ||
10020 element == EL_EXTRA_TIME ||
10021 element == EL_SHIELD_NORMAL ||
10022 element == EL_SHIELD_DEADLY) &&
10023 IS_ANIMATED(graphic))
10024 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10025 else if (IS_MOVING(x, y))
10026 ContinueMoving(x, y);
10027 else if (IS_ACTIVE_BOMB(element))
10028 CheckDynamite(x, y);
10029 else if (element == EL_AMOEBA_GROWING)
10030 AmoebeWaechst(x, y);
10031 else if (element == EL_AMOEBA_SHRINKING)
10032 AmoebaDisappearing(x, y);
10034 #if !USE_NEW_AMOEBA_CODE
10035 else if (IS_AMOEBALIVE(element))
10036 AmoebeAbleger(x, y);
10039 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10041 else if (element == EL_EXIT_CLOSED)
10043 else if (element == EL_SP_EXIT_CLOSED)
10045 else if (element == EL_EXPANDABLE_WALL_GROWING)
10046 MauerWaechst(x, y);
10047 else if (element == EL_EXPANDABLE_WALL ||
10048 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10049 element == EL_EXPANDABLE_WALL_VERTICAL ||
10050 element == EL_EXPANDABLE_WALL_ANY ||
10051 element == EL_BD_EXPANDABLE_WALL)
10052 MauerAbleger(x, y);
10053 else if (element == EL_FLAMES)
10054 CheckForDragon(x, y);
10055 else if (element == EL_EXPLOSION)
10056 ; /* drawing of correct explosion animation is handled separately */
10057 else if (element == EL_ELEMENT_SNAPPING ||
10058 element == EL_DIAGONAL_SHRINKING ||
10059 element == EL_DIAGONAL_GROWING)
10062 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10064 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10067 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10068 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10071 if (element == EL_CUSTOM_255 ||
10072 element == EL_CUSTOM_256)
10073 DrawLevelGraphicAnimation(x, y, graphic);
10076 if (IS_BELT_ACTIVE(element))
10077 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10079 if (game.magic_wall_active)
10081 int jx = local_player->jx, jy = local_player->jy;
10083 /* play the element sound at the position nearest to the player */
10084 if ((element == EL_MAGIC_WALL_FULL ||
10085 element == EL_MAGIC_WALL_ACTIVE ||
10086 element == EL_MAGIC_WALL_EMPTYING ||
10087 element == EL_BD_MAGIC_WALL_FULL ||
10088 element == EL_BD_MAGIC_WALL_ACTIVE ||
10089 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10090 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10098 #if USE_NEW_AMOEBA_CODE
10099 /* new experimental amoeba growth stuff */
10100 if (!(FrameCounter % 8))
10102 static unsigned long random = 1684108901;
10104 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10106 x = RND(lev_fieldx);
10107 y = RND(lev_fieldy);
10108 element = Feld[x][y];
10110 if (!IS_PLAYER(x,y) &&
10111 (element == EL_EMPTY ||
10112 CAN_GROW_INTO(element) ||
10113 element == EL_QUICKSAND_EMPTY ||
10114 element == EL_ACID_SPLASH_LEFT ||
10115 element == EL_ACID_SPLASH_RIGHT))
10117 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10118 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10119 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10120 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10121 Feld[x][y] = EL_AMOEBA_DROP;
10124 random = random * 129 + 1;
10130 if (game.explosions_delayed)
10133 game.explosions_delayed = FALSE;
10136 SCAN_PLAYFIELD(x, y)
10138 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10141 element = Feld[x][y];
10143 if (ExplodeField[x][y])
10144 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10145 else if (element == EL_EXPLOSION)
10146 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10148 ExplodeField[x][y] = EX_TYPE_NONE;
10151 game.explosions_delayed = TRUE;
10154 if (game.magic_wall_active)
10156 if (!(game.magic_wall_time_left % 4))
10158 int element = Feld[magic_wall_x][magic_wall_y];
10160 if (element == EL_BD_MAGIC_WALL_FULL ||
10161 element == EL_BD_MAGIC_WALL_ACTIVE ||
10162 element == EL_BD_MAGIC_WALL_EMPTYING)
10163 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10165 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10168 if (game.magic_wall_time_left > 0)
10170 game.magic_wall_time_left--;
10171 if (!game.magic_wall_time_left)
10174 SCAN_PLAYFIELD(x, y)
10176 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10179 element = Feld[x][y];
10181 if (element == EL_MAGIC_WALL_ACTIVE ||
10182 element == EL_MAGIC_WALL_FULL)
10184 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10185 DrawLevelField(x, y);
10187 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10188 element == EL_BD_MAGIC_WALL_FULL)
10190 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10191 DrawLevelField(x, y);
10195 game.magic_wall_active = FALSE;
10200 if (game.light_time_left > 0)
10202 game.light_time_left--;
10204 if (game.light_time_left == 0)
10205 RedrawAllLightSwitchesAndInvisibleElements();
10208 if (game.timegate_time_left > 0)
10210 game.timegate_time_left--;
10212 if (game.timegate_time_left == 0)
10213 CloseAllOpenTimegates();
10216 if (game.lenses_time_left > 0)
10218 game.lenses_time_left--;
10220 if (game.lenses_time_left == 0)
10221 RedrawAllInvisibleElementsForLenses();
10224 if (game.magnify_time_left > 0)
10226 game.magnify_time_left--;
10228 if (game.magnify_time_left == 0)
10229 RedrawAllInvisibleElementsForMagnifier();
10232 for (i = 0; i < MAX_PLAYERS; i++)
10234 struct PlayerInfo *player = &stored_player[i];
10236 if (SHIELD_ON(player))
10238 if (player->shield_deadly_time_left)
10239 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10240 else if (player->shield_normal_time_left)
10241 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10248 PlayAllPlayersSound();
10250 if (options.debug) /* calculate frames per second */
10252 static unsigned long fps_counter = 0;
10253 static int fps_frames = 0;
10254 unsigned long fps_delay_ms = Counter() - fps_counter;
10258 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10260 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10263 fps_counter = Counter();
10266 redraw_mask |= REDRAW_FPS;
10269 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10271 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10273 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10275 local_player->show_envelope = 0;
10278 /* use random number generator in every frame to make it less predictable */
10279 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10283 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10285 int min_x = x, min_y = y, max_x = x, max_y = y;
10288 for (i = 0; i < MAX_PLAYERS; i++)
10290 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10292 if (!stored_player[i].active || &stored_player[i] == player)
10295 min_x = MIN(min_x, jx);
10296 min_y = MIN(min_y, jy);
10297 max_x = MAX(max_x, jx);
10298 max_y = MAX(max_y, jy);
10301 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10304 static boolean AllPlayersInVisibleScreen()
10308 for (i = 0; i < MAX_PLAYERS; i++)
10310 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10312 if (!stored_player[i].active)
10315 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10322 void ScrollLevel(int dx, int dy)
10324 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10327 BlitBitmap(drawto_field, drawto_field,
10328 FX + TILEX * (dx == -1) - softscroll_offset,
10329 FY + TILEY * (dy == -1) - softscroll_offset,
10330 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10331 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10332 FX + TILEX * (dx == 1) - softscroll_offset,
10333 FY + TILEY * (dy == 1) - softscroll_offset);
10337 x = (dx == 1 ? BX1 : BX2);
10338 for (y = BY1; y <= BY2; y++)
10339 DrawScreenField(x, y);
10344 y = (dy == 1 ? BY1 : BY2);
10345 for (x = BX1; x <= BX2; x++)
10346 DrawScreenField(x, y);
10349 redraw_mask |= REDRAW_FIELD;
10352 static boolean canFallDown(struct PlayerInfo *player)
10354 int jx = player->jx, jy = player->jy;
10356 return (IN_LEV_FIELD(jx, jy + 1) &&
10357 (IS_FREE(jx, jy + 1) ||
10358 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10359 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10360 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10363 static boolean canPassField(int x, int y, int move_dir)
10365 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10366 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10367 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10368 int nextx = x + dx;
10369 int nexty = y + dy;
10370 int element = Feld[x][y];
10372 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10373 !CAN_MOVE(element) &&
10374 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10375 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10376 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10379 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10381 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10382 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10383 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10387 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10388 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10389 (IS_DIGGABLE(Feld[newx][newy]) ||
10390 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10391 canPassField(newx, newy, move_dir)));
10394 static void CheckGravityMovement(struct PlayerInfo *player)
10396 #if USE_PLAYER_GRAVITY
10397 if (player->gravity && !player->programmed_action)
10399 if (game.gravity && !player->programmed_action)
10402 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10403 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10404 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10405 int jx = player->jx, jy = player->jy;
10406 boolean player_is_moving_to_valid_field =
10407 (!player_is_snapping &&
10408 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10409 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10410 boolean player_can_fall_down = canFallDown(player);
10412 if (player_can_fall_down &&
10413 !player_is_moving_to_valid_field)
10414 player->programmed_action = MV_DOWN;
10418 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10420 return CheckGravityMovement(player);
10422 #if USE_PLAYER_GRAVITY
10423 if (player->gravity && !player->programmed_action)
10425 if (game.gravity && !player->programmed_action)
10428 int jx = player->jx, jy = player->jy;
10429 boolean field_under_player_is_free =
10430 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10431 boolean player_is_standing_on_valid_field =
10432 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10433 (IS_WALKABLE(Feld[jx][jy]) &&
10434 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10436 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10437 player->programmed_action = MV_DOWN;
10442 MovePlayerOneStep()
10443 -----------------------------------------------------------------------------
10444 dx, dy: direction (non-diagonal) to try to move the player to
10445 real_dx, real_dy: direction as read from input device (can be diagonal)
10448 boolean MovePlayerOneStep(struct PlayerInfo *player,
10449 int dx, int dy, int real_dx, int real_dy)
10451 int jx = player->jx, jy = player->jy;
10452 int new_jx = jx + dx, new_jy = jy + dy;
10453 #if !USE_FIXED_DONT_RUN_INTO
10457 boolean player_can_move = !player->cannot_move;
10459 if (!player->active || (!dx && !dy))
10460 return MP_NO_ACTION;
10462 player->MovDir = (dx < 0 ? MV_LEFT :
10463 dx > 0 ? MV_RIGHT :
10465 dy > 0 ? MV_DOWN : MV_NONE);
10467 if (!IN_LEV_FIELD(new_jx, new_jy))
10468 return MP_NO_ACTION;
10470 if (!player_can_move)
10473 if (player->MovPos == 0)
10475 player->is_moving = FALSE;
10476 player->is_digging = FALSE;
10477 player->is_collecting = FALSE;
10478 player->is_snapping = FALSE;
10479 player->is_pushing = FALSE;
10482 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10483 SnapField(player, 0, 0);
10487 return MP_NO_ACTION;
10492 if (!options.network && game.centered_player_nr == -1 &&
10493 !AllPlayersInSight(player, new_jx, new_jy))
10494 return MP_NO_ACTION;
10496 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10497 return MP_NO_ACTION;
10500 #if !USE_FIXED_DONT_RUN_INTO
10501 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10503 /* (moved to DigField()) */
10504 if (player_can_move && DONT_RUN_INTO(element))
10506 if (element == EL_ACID && dx == 0 && dy == 1)
10508 SplashAcid(new_jx, new_jy);
10509 Feld[jx][jy] = EL_PLAYER_1;
10510 InitMovingField(jx, jy, MV_DOWN);
10511 Store[jx][jy] = EL_ACID;
10512 ContinueMoving(jx, jy);
10513 BuryPlayer(player);
10516 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10522 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10524 #if USE_FIXED_DONT_RUN_INTO
10525 if (can_move == MP_DONT_RUN_INTO)
10529 if (can_move != MP_MOVING)
10532 #if USE_FIXED_DONT_RUN_INTO
10535 /* check if DigField() has caused relocation of the player */
10536 if (player->jx != jx || player->jy != jy)
10537 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10539 StorePlayer[jx][jy] = 0;
10540 player->last_jx = jx;
10541 player->last_jy = jy;
10542 player->jx = new_jx;
10543 player->jy = new_jy;
10544 StorePlayer[new_jx][new_jy] = player->element_nr;
10546 if (player->move_delay_value_next != -1)
10548 player->move_delay_value = player->move_delay_value_next;
10549 player->move_delay_value_next = -1;
10553 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10555 player->step_counter++;
10557 PlayerVisit[jx][jy] = FrameCounter;
10559 ScrollPlayer(player, SCROLL_INIT);
10564 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10566 int jx = player->jx, jy = player->jy;
10567 int old_jx = jx, old_jy = jy;
10568 int moved = MP_NO_ACTION;
10570 if (!player->active)
10575 if (player->MovPos == 0)
10577 player->is_moving = FALSE;
10578 player->is_digging = FALSE;
10579 player->is_collecting = FALSE;
10580 player->is_snapping = FALSE;
10581 player->is_pushing = FALSE;
10587 if (player->move_delay > 0)
10590 player->move_delay = -1; /* set to "uninitialized" value */
10592 /* store if player is automatically moved to next field */
10593 player->is_auto_moving = (player->programmed_action != MV_NONE);
10595 /* remove the last programmed player action */
10596 player->programmed_action = 0;
10598 if (player->MovPos)
10600 /* should only happen if pre-1.2 tape recordings are played */
10601 /* this is only for backward compatibility */
10603 int original_move_delay_value = player->move_delay_value;
10606 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10610 /* scroll remaining steps with finest movement resolution */
10611 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10613 while (player->MovPos)
10615 ScrollPlayer(player, SCROLL_GO_ON);
10616 ScrollScreen(NULL, SCROLL_GO_ON);
10618 AdvanceFrameAndPlayerCounters(player->index_nr);
10624 player->move_delay_value = original_move_delay_value;
10627 player->is_active = FALSE;
10629 if (player->last_move_dir & MV_HORIZONTAL)
10631 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10632 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10636 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10637 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10640 #if USE_FIXED_BORDER_RUNNING_GFX
10641 if (!moved && !player->is_active)
10643 player->is_moving = FALSE;
10644 player->is_digging = FALSE;
10645 player->is_collecting = FALSE;
10646 player->is_snapping = FALSE;
10647 player->is_pushing = FALSE;
10655 if (moved & MP_MOVING && !ScreenMovPos &&
10656 (player->index_nr == game.centered_player_nr ||
10657 game.centered_player_nr == -1))
10659 if (moved & MP_MOVING && !ScreenMovPos &&
10660 (player == local_player || !options.network))
10663 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10664 int offset = (setup.scroll_delay ? 3 : 0);
10666 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10668 /* actual player has left the screen -- scroll in that direction */
10669 if (jx != old_jx) /* player has moved horizontally */
10670 scroll_x += (jx - old_jx);
10671 else /* player has moved vertically */
10672 scroll_y += (jy - old_jy);
10676 if (jx != old_jx) /* player has moved horizontally */
10678 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10679 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10680 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10682 /* don't scroll over playfield boundaries */
10683 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10684 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10686 /* don't scroll more than one field at a time */
10687 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10689 /* don't scroll against the player's moving direction */
10690 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10691 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10692 scroll_x = old_scroll_x;
10694 else /* player has moved vertically */
10696 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10697 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10698 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10700 /* don't scroll over playfield boundaries */
10701 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10702 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10704 /* don't scroll more than one field at a time */
10705 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10707 /* don't scroll against the player's moving direction */
10708 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10709 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10710 scroll_y = old_scroll_y;
10714 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10717 if (!options.network && game.centered_player_nr == -1 &&
10718 !AllPlayersInVisibleScreen())
10720 scroll_x = old_scroll_x;
10721 scroll_y = old_scroll_y;
10725 if (!options.network && !AllPlayersInVisibleScreen())
10727 scroll_x = old_scroll_x;
10728 scroll_y = old_scroll_y;
10733 ScrollScreen(player, SCROLL_INIT);
10734 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10739 player->StepFrame = 0;
10741 if (moved & MP_MOVING)
10743 if (old_jx != jx && old_jy == jy)
10744 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10745 else if (old_jx == jx && old_jy != jy)
10746 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10748 DrawLevelField(jx, jy); /* for "crumbled sand" */
10750 player->last_move_dir = player->MovDir;
10751 player->is_moving = TRUE;
10752 player->is_snapping = FALSE;
10753 player->is_switching = FALSE;
10754 player->is_dropping = FALSE;
10755 player->is_dropping_pressed = FALSE;
10756 player->drop_pressed_delay = 0;
10760 CheckGravityMovementWhenNotMoving(player);
10762 player->is_moving = FALSE;
10764 /* at this point, the player is allowed to move, but cannot move right now
10765 (e.g. because of something blocking the way) -- ensure that the player
10766 is also allowed to move in the next frame (in old versions before 3.1.1,
10767 the player was forced to wait again for eight frames before next try) */
10769 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10770 player->move_delay = 0; /* allow direct movement in the next frame */
10773 if (player->move_delay == -1) /* not yet initialized by DigField() */
10774 player->move_delay = player->move_delay_value;
10776 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10778 TestIfPlayerTouchesBadThing(jx, jy);
10779 TestIfPlayerTouchesCustomElement(jx, jy);
10782 if (!player->active)
10783 RemovePlayer(player);
10788 void ScrollPlayer(struct PlayerInfo *player, int mode)
10790 int jx = player->jx, jy = player->jy;
10791 int last_jx = player->last_jx, last_jy = player->last_jy;
10792 int move_stepsize = TILEX / player->move_delay_value;
10794 #if USE_NEW_PLAYER_SPEED
10795 if (!player->active)
10798 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10801 if (!player->active || player->MovPos == 0)
10805 if (mode == SCROLL_INIT)
10807 player->actual_frame_counter = FrameCounter;
10808 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10810 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10811 Feld[last_jx][last_jy] == EL_EMPTY)
10813 int last_field_block_delay = 0; /* start with no blocking at all */
10814 int block_delay_adjustment = player->block_delay_adjustment;
10816 /* if player blocks last field, add delay for exactly one move */
10817 if (player->block_last_field)
10819 last_field_block_delay += player->move_delay_value;
10821 /* when blocking enabled, prevent moving up despite gravity */
10822 #if USE_PLAYER_GRAVITY
10823 if (player->gravity && player->MovDir == MV_UP)
10824 block_delay_adjustment = -1;
10826 if (game.gravity && player->MovDir == MV_UP)
10827 block_delay_adjustment = -1;
10831 /* add block delay adjustment (also possible when not blocking) */
10832 last_field_block_delay += block_delay_adjustment;
10834 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10835 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10838 #if USE_NEW_PLAYER_SPEED
10839 if (player->MovPos != 0) /* player has not yet reached destination */
10845 else if (!FrameReached(&player->actual_frame_counter, 1))
10849 printf("::: player->MovPos: %d -> %d\n",
10851 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10854 #if USE_NEW_PLAYER_SPEED
10855 if (player->MovPos != 0)
10857 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10858 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10860 /* before DrawPlayer() to draw correct player graphic for this case */
10861 if (player->MovPos == 0)
10862 CheckGravityMovement(player);
10865 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10866 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10868 /* before DrawPlayer() to draw correct player graphic for this case */
10869 if (player->MovPos == 0)
10870 CheckGravityMovement(player);
10873 if (player->MovPos == 0) /* player reached destination field */
10876 printf("::: player reached destination field\n");
10879 if (player->move_delay_reset_counter > 0)
10881 player->move_delay_reset_counter--;
10883 if (player->move_delay_reset_counter == 0)
10885 /* continue with normal speed after quickly moving through gate */
10886 HALVE_PLAYER_SPEED(player);
10888 /* be able to make the next move without delay */
10889 player->move_delay = 0;
10893 player->last_jx = jx;
10894 player->last_jy = jy;
10896 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10897 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10898 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10900 DrawPlayer(player); /* needed here only to cleanup last field */
10901 RemovePlayer(player);
10903 if (local_player->friends_still_needed == 0 ||
10904 IS_SP_ELEMENT(Feld[jx][jy]))
10905 player->LevelSolved = player->GameOver = TRUE;
10908 /* this breaks one level: "machine", level 000 */
10910 int move_direction = player->MovDir;
10911 int enter_side = MV_DIR_OPPOSITE(move_direction);
10912 int leave_side = move_direction;
10913 int old_jx = last_jx;
10914 int old_jy = last_jy;
10915 int old_element = Feld[old_jx][old_jy];
10916 int new_element = Feld[jx][jy];
10918 if (IS_CUSTOM_ELEMENT(old_element))
10919 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10921 player->index_bit, leave_side);
10923 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10924 CE_PLAYER_LEAVES_X,
10925 player->index_bit, leave_side);
10927 if (IS_CUSTOM_ELEMENT(new_element))
10928 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10929 player->index_bit, enter_side);
10931 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10932 CE_PLAYER_ENTERS_X,
10933 player->index_bit, enter_side);
10935 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10936 CE_MOVE_OF_X, move_direction);
10939 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10941 TestIfPlayerTouchesBadThing(jx, jy);
10942 TestIfPlayerTouchesCustomElement(jx, jy);
10944 /* needed because pushed element has not yet reached its destination,
10945 so it would trigger a change event at its previous field location */
10946 if (!player->is_pushing)
10947 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10949 if (!player->active)
10950 RemovePlayer(player);
10953 if (level.use_step_counter)
10963 if (TimeLeft <= 10 && setup.time_limit)
10964 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10966 DrawGameValue_Time(TimeLeft);
10968 if (!TimeLeft && setup.time_limit)
10969 for (i = 0; i < MAX_PLAYERS; i++)
10970 KillPlayer(&stored_player[i]);
10972 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10973 DrawGameValue_Time(TimePlayed);
10976 if (tape.single_step && tape.recording && !tape.pausing &&
10977 !player->programmed_action)
10978 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10982 void ScrollScreen(struct PlayerInfo *player, int mode)
10984 static unsigned long screen_frame_counter = 0;
10986 if (mode == SCROLL_INIT)
10988 /* set scrolling step size according to actual player's moving speed */
10989 ScrollStepSize = TILEX / player->move_delay_value;
10991 screen_frame_counter = FrameCounter;
10992 ScreenMovDir = player->MovDir;
10993 ScreenMovPos = player->MovPos;
10994 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10997 else if (!FrameReached(&screen_frame_counter, 1))
11002 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11003 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11004 redraw_mask |= REDRAW_FIELD;
11007 ScreenMovDir = MV_NONE;
11010 void TestIfPlayerTouchesCustomElement(int x, int y)
11012 static int xy[4][2] =
11019 static int trigger_sides[4][2] =
11021 /* center side border side */
11022 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11023 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11024 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11025 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11027 static int touch_dir[4] =
11029 MV_LEFT | MV_RIGHT,
11034 int center_element = Feld[x][y]; /* should always be non-moving! */
11037 for (i = 0; i < NUM_DIRECTIONS; i++)
11039 int xx = x + xy[i][0];
11040 int yy = y + xy[i][1];
11041 int center_side = trigger_sides[i][0];
11042 int border_side = trigger_sides[i][1];
11043 int border_element;
11045 if (!IN_LEV_FIELD(xx, yy))
11048 if (IS_PLAYER(x, y))
11050 struct PlayerInfo *player = PLAYERINFO(x, y);
11052 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11053 border_element = Feld[xx][yy]; /* may be moving! */
11054 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11055 border_element = Feld[xx][yy];
11056 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11057 border_element = MovingOrBlocked2Element(xx, yy);
11059 continue; /* center and border element do not touch */
11061 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11062 player->index_bit, border_side);
11063 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11064 CE_PLAYER_TOUCHES_X,
11065 player->index_bit, border_side);
11067 else if (IS_PLAYER(xx, yy))
11069 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11071 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11073 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11074 continue; /* center and border element do not touch */
11077 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11078 player->index_bit, center_side);
11079 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11080 CE_PLAYER_TOUCHES_X,
11081 player->index_bit, center_side);
11087 #if USE_ELEMENT_TOUCHING_BUGFIX
11089 void TestIfElementTouchesCustomElement(int x, int y)
11091 static int xy[4][2] =
11098 static int trigger_sides[4][2] =
11100 /* center side border side */
11101 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11102 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11103 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11104 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11106 static int touch_dir[4] =
11108 MV_LEFT | MV_RIGHT,
11113 boolean change_center_element = FALSE;
11114 int center_element = Feld[x][y]; /* should always be non-moving! */
11115 int border_element_old[NUM_DIRECTIONS];
11118 for (i = 0; i < NUM_DIRECTIONS; i++)
11120 int xx = x + xy[i][0];
11121 int yy = y + xy[i][1];
11122 int border_element;
11124 border_element_old[i] = -1;
11126 if (!IN_LEV_FIELD(xx, yy))
11129 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11130 border_element = Feld[xx][yy]; /* may be moving! */
11131 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11132 border_element = Feld[xx][yy];
11133 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11134 border_element = MovingOrBlocked2Element(xx, yy);
11136 continue; /* center and border element do not touch */
11138 border_element_old[i] = border_element;
11141 for (i = 0; i < NUM_DIRECTIONS; i++)
11143 int xx = x + xy[i][0];
11144 int yy = y + xy[i][1];
11145 int center_side = trigger_sides[i][0];
11146 int border_element = border_element_old[i];
11148 if (border_element == -1)
11151 /* check for change of border element */
11152 CheckElementChangeBySide(xx, yy, border_element, center_element,
11153 CE_TOUCHING_X, center_side);
11156 for (i = 0; i < NUM_DIRECTIONS; i++)
11158 int border_side = trigger_sides[i][1];
11159 int border_element = border_element_old[i];
11161 if (border_element == -1)
11164 /* check for change of center element (but change it only once) */
11165 if (!change_center_element)
11166 change_center_element =
11167 CheckElementChangeBySide(x, y, center_element, border_element,
11168 CE_TOUCHING_X, border_side);
11174 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11176 static int xy[4][2] =
11183 static int trigger_sides[4][2] =
11185 /* center side border side */
11186 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11187 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11188 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11189 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11191 static int touch_dir[4] =
11193 MV_LEFT | MV_RIGHT,
11198 boolean change_center_element = FALSE;
11199 int center_element = Feld[x][y]; /* should always be non-moving! */
11202 for (i = 0; i < NUM_DIRECTIONS; i++)
11204 int xx = x + xy[i][0];
11205 int yy = y + xy[i][1];
11206 int center_side = trigger_sides[i][0];
11207 int border_side = trigger_sides[i][1];
11208 int border_element;
11210 if (!IN_LEV_FIELD(xx, yy))
11213 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11214 border_element = Feld[xx][yy]; /* may be moving! */
11215 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11216 border_element = Feld[xx][yy];
11217 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11218 border_element = MovingOrBlocked2Element(xx, yy);
11220 continue; /* center and border element do not touch */
11222 /* check for change of center element (but change it only once) */
11223 if (!change_center_element)
11224 change_center_element =
11225 CheckElementChangeBySide(x, y, center_element, border_element,
11226 CE_TOUCHING_X, border_side);
11228 /* check for change of border element */
11229 CheckElementChangeBySide(xx, yy, border_element, center_element,
11230 CE_TOUCHING_X, center_side);
11236 void TestIfElementHitsCustomElement(int x, int y, int direction)
11238 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11239 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11240 int hitx = x + dx, hity = y + dy;
11241 int hitting_element = Feld[x][y];
11242 int touched_element;
11244 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11247 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11248 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11250 if (IN_LEV_FIELD(hitx, hity))
11252 int opposite_direction = MV_DIR_OPPOSITE(direction);
11253 int hitting_side = direction;
11254 int touched_side = opposite_direction;
11255 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11256 MovDir[hitx][hity] != direction ||
11257 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11263 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11264 CE_HITTING_X, touched_side);
11266 CheckElementChangeBySide(hitx, hity, touched_element,
11267 hitting_element, CE_HIT_BY_X, hitting_side);
11269 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11270 CE_HIT_BY_SOMETHING, opposite_direction);
11274 /* "hitting something" is also true when hitting the playfield border */
11275 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11276 CE_HITTING_SOMETHING, direction);
11280 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11282 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11283 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11284 int hitx = x + dx, hity = y + dy;
11285 int hitting_element = Feld[x][y];
11286 int touched_element;
11288 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11289 !IS_FREE(hitx, hity) &&
11290 (!IS_MOVING(hitx, hity) ||
11291 MovDir[hitx][hity] != direction ||
11292 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11295 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11299 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11303 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11304 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11306 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11307 EP_CAN_SMASH_EVERYTHING, direction);
11309 if (IN_LEV_FIELD(hitx, hity))
11311 int opposite_direction = MV_DIR_OPPOSITE(direction);
11312 int hitting_side = direction;
11313 int touched_side = opposite_direction;
11315 int touched_element = MovingOrBlocked2Element(hitx, hity);
11318 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11319 MovDir[hitx][hity] != direction ||
11320 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11329 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11330 CE_SMASHED_BY_SOMETHING, opposite_direction);
11332 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11333 CE_OTHER_IS_SMASHING, touched_side);
11335 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11336 CE_OTHER_GETS_SMASHED, hitting_side);
11342 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11344 int i, kill_x = -1, kill_y = -1;
11346 int bad_element = -1;
11347 static int test_xy[4][2] =
11354 static int test_dir[4] =
11362 for (i = 0; i < NUM_DIRECTIONS; i++)
11364 int test_x, test_y, test_move_dir, test_element;
11366 test_x = good_x + test_xy[i][0];
11367 test_y = good_y + test_xy[i][1];
11369 if (!IN_LEV_FIELD(test_x, test_y))
11373 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11375 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11377 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11378 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11380 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11381 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11385 bad_element = test_element;
11391 if (kill_x != -1 || kill_y != -1)
11393 if (IS_PLAYER(good_x, good_y))
11395 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11397 if (player->shield_deadly_time_left > 0 &&
11398 !IS_INDESTRUCTIBLE(bad_element))
11399 Bang(kill_x, kill_y);
11400 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11401 KillPlayer(player);
11404 Bang(good_x, good_y);
11408 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11410 int i, kill_x = -1, kill_y = -1;
11411 int bad_element = Feld[bad_x][bad_y];
11412 static int test_xy[4][2] =
11419 static int touch_dir[4] =
11421 MV_LEFT | MV_RIGHT,
11426 static int test_dir[4] =
11434 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11437 for (i = 0; i < NUM_DIRECTIONS; i++)
11439 int test_x, test_y, test_move_dir, test_element;
11441 test_x = bad_x + test_xy[i][0];
11442 test_y = bad_y + test_xy[i][1];
11443 if (!IN_LEV_FIELD(test_x, test_y))
11447 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11449 test_element = Feld[test_x][test_y];
11451 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11452 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11454 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11455 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11457 /* good thing is player or penguin that does not move away */
11458 if (IS_PLAYER(test_x, test_y))
11460 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11462 if (bad_element == EL_ROBOT && player->is_moving)
11463 continue; /* robot does not kill player if he is moving */
11465 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11467 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11468 continue; /* center and border element do not touch */
11475 else if (test_element == EL_PENGUIN)
11484 if (kill_x != -1 || kill_y != -1)
11486 if (IS_PLAYER(kill_x, kill_y))
11488 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11490 if (player->shield_deadly_time_left > 0 &&
11491 !IS_INDESTRUCTIBLE(bad_element))
11492 Bang(bad_x, bad_y);
11493 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11494 KillPlayer(player);
11497 Bang(kill_x, kill_y);
11501 void TestIfPlayerTouchesBadThing(int x, int y)
11503 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11506 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11508 TestIfGoodThingHitsBadThing(x, y, move_dir);
11511 void TestIfBadThingTouchesPlayer(int x, int y)
11513 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11516 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11518 TestIfBadThingHitsGoodThing(x, y, move_dir);
11521 void TestIfFriendTouchesBadThing(int x, int y)
11523 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11526 void TestIfBadThingTouchesFriend(int x, int y)
11528 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11531 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11533 int i, kill_x = bad_x, kill_y = bad_y;
11534 static int xy[4][2] =
11542 for (i = 0; i < NUM_DIRECTIONS; i++)
11546 x = bad_x + xy[i][0];
11547 y = bad_y + xy[i][1];
11548 if (!IN_LEV_FIELD(x, y))
11551 element = Feld[x][y];
11552 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11553 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11561 if (kill_x != bad_x || kill_y != bad_y)
11562 Bang(bad_x, bad_y);
11565 void KillPlayer(struct PlayerInfo *player)
11567 int jx = player->jx, jy = player->jy;
11569 if (!player->active)
11572 /* remove accessible field at the player's position */
11573 Feld[jx][jy] = EL_EMPTY;
11575 /* deactivate shield (else Bang()/Explode() would not work right) */
11576 player->shield_normal_time_left = 0;
11577 player->shield_deadly_time_left = 0;
11580 BuryPlayer(player);
11583 static void KillPlayerUnlessEnemyProtected(int x, int y)
11585 if (!PLAYER_ENEMY_PROTECTED(x, y))
11586 KillPlayer(PLAYERINFO(x, y));
11589 static void KillPlayerUnlessExplosionProtected(int x, int y)
11591 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11592 KillPlayer(PLAYERINFO(x, y));
11595 void BuryPlayer(struct PlayerInfo *player)
11597 int jx = player->jx, jy = player->jy;
11599 if (!player->active)
11602 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11603 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11605 player->GameOver = TRUE;
11606 RemovePlayer(player);
11609 void RemovePlayer(struct PlayerInfo *player)
11611 int jx = player->jx, jy = player->jy;
11612 int i, found = FALSE;
11614 player->present = FALSE;
11615 player->active = FALSE;
11617 if (!ExplodeField[jx][jy])
11618 StorePlayer[jx][jy] = 0;
11620 if (player->is_moving)
11621 DrawLevelField(player->last_jx, player->last_jy);
11623 for (i = 0; i < MAX_PLAYERS; i++)
11624 if (stored_player[i].active)
11628 AllPlayersGone = TRUE;
11634 #if USE_NEW_SNAP_DELAY
11635 static void setFieldForSnapping(int x, int y, int element, int direction)
11637 struct ElementInfo *ei = &element_info[element];
11638 int direction_bit = MV_DIR_TO_BIT(direction);
11639 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11640 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11641 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11643 Feld[x][y] = EL_ELEMENT_SNAPPING;
11644 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11646 ResetGfxAnimation(x, y);
11648 GfxElement[x][y] = element;
11649 GfxAction[x][y] = action;
11650 GfxDir[x][y] = direction;
11651 GfxFrame[x][y] = -1;
11656 =============================================================================
11657 checkDiagonalPushing()
11658 -----------------------------------------------------------------------------
11659 check if diagonal input device direction results in pushing of object
11660 (by checking if the alternative direction is walkable, diggable, ...)
11661 =============================================================================
11664 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11665 int x, int y, int real_dx, int real_dy)
11667 int jx, jy, dx, dy, xx, yy;
11669 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11672 /* diagonal direction: check alternative direction */
11677 xx = jx + (dx == 0 ? real_dx : 0);
11678 yy = jy + (dy == 0 ? real_dy : 0);
11680 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11684 =============================================================================
11686 -----------------------------------------------------------------------------
11687 x, y: field next to player (non-diagonal) to try to dig to
11688 real_dx, real_dy: direction as read from input device (can be diagonal)
11689 =============================================================================
11692 int DigField(struct PlayerInfo *player,
11693 int oldx, int oldy, int x, int y,
11694 int real_dx, int real_dy, int mode)
11696 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11697 boolean player_was_pushing = player->is_pushing;
11698 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11699 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11700 int jx = oldx, jy = oldy;
11701 int dx = x - jx, dy = y - jy;
11702 int nextx = x + dx, nexty = y + dy;
11703 int move_direction = (dx == -1 ? MV_LEFT :
11704 dx == +1 ? MV_RIGHT :
11706 dy == +1 ? MV_DOWN : MV_NONE);
11707 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11708 int dig_side = MV_DIR_OPPOSITE(move_direction);
11709 int old_element = Feld[jx][jy];
11710 #if USE_FIXED_DONT_RUN_INTO
11711 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11717 if (is_player) /* function can also be called by EL_PENGUIN */
11719 if (player->MovPos == 0)
11721 player->is_digging = FALSE;
11722 player->is_collecting = FALSE;
11725 if (player->MovPos == 0) /* last pushing move finished */
11726 player->is_pushing = FALSE;
11728 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11730 player->is_switching = FALSE;
11731 player->push_delay = -1;
11733 return MP_NO_ACTION;
11737 #if !USE_FIXED_DONT_RUN_INTO
11738 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11739 return MP_NO_ACTION;
11742 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11743 old_element = Back[jx][jy];
11745 /* in case of element dropped at player position, check background */
11746 else if (Back[jx][jy] != EL_EMPTY &&
11747 game.engine_version >= VERSION_IDENT(2,2,0,0))
11748 old_element = Back[jx][jy];
11750 /* checking here causes player to move into acid even if the current field
11751 cannot be left to that direction */
11753 #if USE_FIXED_DONT_RUN_INTO
11754 if (player_can_move && DONT_RUN_INTO(element))
11756 if (element == EL_ACID && dx == 0 && dy == 1)
11759 Feld[jx][jy] = EL_PLAYER_1;
11760 InitMovingField(jx, jy, MV_DOWN);
11761 Store[jx][jy] = EL_ACID;
11762 ContinueMoving(jx, jy);
11763 BuryPlayer(player);
11766 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11768 return MP_DONT_RUN_INTO;
11773 #if 1 /* ------------------------------ NEW ------------------------------ */
11775 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11776 return MP_NO_ACTION; /* field has no opening in this direction */
11778 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11779 return MP_NO_ACTION; /* field has no opening in this direction */
11781 #if USE_FIXED_DONT_RUN_INTO
11782 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11786 Feld[jx][jy] = player->artwork_element;
11788 Feld[jx][jy] = EL_PLAYER_1;
11790 InitMovingField(jx, jy, MV_DOWN);
11791 Store[jx][jy] = EL_ACID;
11792 ContinueMoving(jx, jy);
11793 BuryPlayer(player);
11795 return MP_DONT_RUN_INTO;
11799 #if USE_FIXED_DONT_RUN_INTO
11800 if (player_can_move && DONT_RUN_INTO(element))
11802 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11804 return MP_DONT_RUN_INTO;
11808 #else /* ------------------------------ OLD ------------------------------ */
11811 #if USE_FIXED_DONT_RUN_INTO
11812 if (player_can_move && DONT_RUN_INTO(element))
11814 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11816 return MP_DONT_RUN_INTO;
11821 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11822 return MP_NO_ACTION; /* field has no opening in this direction */
11824 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11825 return MP_NO_ACTION; /* field has no opening in this direction */
11827 /* checking here causes player to explode when moving into acid */
11829 #if USE_FIXED_DONT_RUN_INTO
11830 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11833 Feld[jx][jy] = EL_PLAYER_1;
11834 InitMovingField(jx, jy, MV_DOWN);
11835 Store[jx][jy] = EL_ACID;
11836 ContinueMoving(jx, jy);
11837 BuryPlayer(player);
11839 return MP_DONT_RUN_INTO;
11844 #endif /* ------------------------------ END ------------------------------ */
11847 #if USE_FIXED_DONT_RUN_INTO
11848 if (player_can_move && DONT_RUN_INTO(element))
11850 if (element == EL_ACID && dx == 0 && dy == 1)
11853 Feld[jx][jy] = EL_PLAYER_1;
11854 InitMovingField(jx, jy, MV_DOWN);
11855 Store[jx][jy] = EL_ACID;
11856 ContinueMoving(jx, jy);
11857 BuryPlayer(player);
11860 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11862 return MP_DONT_RUN_INTO;
11867 #if USE_FIXED_DONT_RUN_INTO
11868 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11869 return MP_NO_ACTION;
11872 #if !USE_FIXED_DONT_RUN_INTO
11873 element = Feld[x][y];
11876 collect_count = element_info[element].collect_count_initial;
11878 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11879 return MP_NO_ACTION;
11881 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11882 player_can_move = player_can_move_or_snap;
11884 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11885 game.engine_version >= VERSION_IDENT(2,2,0,0))
11887 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11888 player->index_bit, dig_side);
11889 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11890 player->index_bit, dig_side);
11892 if (Feld[x][y] != element) /* field changed by snapping */
11895 return MP_NO_ACTION;
11898 #if USE_PLAYER_GRAVITY
11899 if (player->gravity && is_player && !player->is_auto_moving &&
11900 canFallDown(player) && move_direction != MV_DOWN &&
11901 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11902 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11904 if (game.gravity && is_player && !player->is_auto_moving &&
11905 canFallDown(player) && move_direction != MV_DOWN &&
11906 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11907 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11910 if (player_can_move &&
11911 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11913 int sound_element = SND_ELEMENT(element);
11914 int sound_action = ACTION_WALKING;
11916 if (IS_RND_GATE(element))
11918 if (!player->key[RND_GATE_NR(element)])
11919 return MP_NO_ACTION;
11921 else if (IS_RND_GATE_GRAY(element))
11923 if (!player->key[RND_GATE_GRAY_NR(element)])
11924 return MP_NO_ACTION;
11926 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11928 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11929 return MP_NO_ACTION;
11931 else if (element == EL_EXIT_OPEN ||
11932 element == EL_SP_EXIT_OPEN ||
11933 element == EL_SP_EXIT_OPENING)
11935 sound_action = ACTION_PASSING; /* player is passing exit */
11937 else if (element == EL_EMPTY)
11939 sound_action = ACTION_MOVING; /* nothing to walk on */
11942 /* play sound from background or player, whatever is available */
11943 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11944 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11946 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11948 else if (player_can_move &&
11949 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11951 if (!ACCESS_FROM(element, opposite_direction))
11952 return MP_NO_ACTION; /* field not accessible from this direction */
11954 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11955 return MP_NO_ACTION;
11957 if (IS_EM_GATE(element))
11959 if (!player->key[EM_GATE_NR(element)])
11960 return MP_NO_ACTION;
11962 else if (IS_EM_GATE_GRAY(element))
11964 if (!player->key[EM_GATE_GRAY_NR(element)])
11965 return MP_NO_ACTION;
11967 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11969 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11970 return MP_NO_ACTION;
11972 else if (IS_EMC_GATE(element))
11974 if (!player->key[EMC_GATE_NR(element)])
11975 return MP_NO_ACTION;
11977 else if (IS_EMC_GATE_GRAY(element))
11979 if (!player->key[EMC_GATE_GRAY_NR(element)])
11980 return MP_NO_ACTION;
11982 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11984 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11985 return MP_NO_ACTION;
11987 else if (IS_SP_PORT(element))
11989 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11990 element == EL_SP_GRAVITY_PORT_RIGHT ||
11991 element == EL_SP_GRAVITY_PORT_UP ||
11992 element == EL_SP_GRAVITY_PORT_DOWN)
11993 #if USE_PLAYER_GRAVITY
11994 player->gravity = !player->gravity;
11996 game.gravity = !game.gravity;
11998 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11999 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12000 element == EL_SP_GRAVITY_ON_PORT_UP ||
12001 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12002 #if USE_PLAYER_GRAVITY
12003 player->gravity = TRUE;
12005 game.gravity = TRUE;
12007 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12008 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12009 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12010 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12011 #if USE_PLAYER_GRAVITY
12012 player->gravity = FALSE;
12014 game.gravity = FALSE;
12018 /* automatically move to the next field with double speed */
12019 player->programmed_action = move_direction;
12021 if (player->move_delay_reset_counter == 0)
12023 player->move_delay_reset_counter = 2; /* two double speed steps */
12025 DOUBLE_PLAYER_SPEED(player);
12028 PlayLevelSoundAction(x, y, ACTION_PASSING);
12030 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12034 if (mode != DF_SNAP)
12036 GfxElement[x][y] = GFX_ELEMENT(element);
12037 player->is_digging = TRUE;
12040 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12042 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12043 player->index_bit, dig_side);
12045 if (mode == DF_SNAP)
12047 #if USE_NEW_SNAP_DELAY
12048 if (level.block_snap_field)
12049 setFieldForSnapping(x, y, element, move_direction);
12051 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12053 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12056 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12057 player->index_bit, dig_side);
12060 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12064 if (is_player && mode != DF_SNAP)
12066 GfxElement[x][y] = element;
12067 player->is_collecting = TRUE;
12070 if (element == EL_SPEED_PILL)
12072 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12074 else if (element == EL_EXTRA_TIME && level.time > 0)
12076 TimeLeft += level.extra_time;
12077 DrawGameValue_Time(TimeLeft);
12079 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12081 player->shield_normal_time_left += level.shield_normal_time;
12082 if (element == EL_SHIELD_DEADLY)
12083 player->shield_deadly_time_left += level.shield_deadly_time;
12085 else if (element == EL_DYNAMITE ||
12086 element == EL_EM_DYNAMITE ||
12087 element == EL_SP_DISK_RED)
12089 if (player->inventory_size < MAX_INVENTORY_SIZE)
12090 player->inventory_element[player->inventory_size++] = element;
12093 DrawGameDoorValues();
12095 DrawGameValue_Dynamite(local_player->inventory_size);
12098 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12100 player->dynabomb_count++;
12101 player->dynabombs_left++;
12103 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12105 player->dynabomb_size++;
12107 else if (element == EL_DYNABOMB_INCREASE_POWER)
12109 player->dynabomb_xl = TRUE;
12111 else if (IS_KEY(element))
12113 player->key[KEY_NR(element)] = TRUE;
12116 DrawGameDoorValues();
12118 DrawGameValue_Keys(player->key);
12121 redraw_mask |= REDRAW_DOOR_1;
12123 else if (IS_ENVELOPE(element))
12125 player->show_envelope = element;
12127 else if (element == EL_EMC_LENSES)
12129 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12131 RedrawAllInvisibleElementsForLenses();
12133 else if (element == EL_EMC_MAGNIFIER)
12135 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12137 RedrawAllInvisibleElementsForMagnifier();
12139 else if (IS_DROPPABLE(element) ||
12140 IS_THROWABLE(element)) /* can be collected and dropped */
12144 if (collect_count == 0)
12145 player->inventory_infinite_element = element;
12147 for (i = 0; i < collect_count; i++)
12148 if (player->inventory_size < MAX_INVENTORY_SIZE)
12149 player->inventory_element[player->inventory_size++] = element;
12152 DrawGameDoorValues();
12154 DrawGameValue_Dynamite(local_player->inventory_size);
12157 else if (collect_count > 0)
12159 local_player->gems_still_needed -= collect_count;
12160 if (local_player->gems_still_needed < 0)
12161 local_player->gems_still_needed = 0;
12163 DrawGameValue_Emeralds(local_player->gems_still_needed);
12166 RaiseScoreElement(element);
12167 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12170 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12171 player->index_bit, dig_side);
12173 if (mode == DF_SNAP)
12175 #if USE_NEW_SNAP_DELAY
12176 if (level.block_snap_field)
12177 setFieldForSnapping(x, y, element, move_direction);
12179 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12181 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12184 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12185 player->index_bit, dig_side);
12188 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12190 if (mode == DF_SNAP && element != EL_BD_ROCK)
12191 return MP_NO_ACTION;
12193 if (CAN_FALL(element) && dy)
12194 return MP_NO_ACTION;
12196 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12197 !(element == EL_SPRING && level.use_spring_bug))
12198 return MP_NO_ACTION;
12200 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12201 ((move_direction & MV_VERTICAL &&
12202 ((element_info[element].move_pattern & MV_LEFT &&
12203 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12204 (element_info[element].move_pattern & MV_RIGHT &&
12205 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12206 (move_direction & MV_HORIZONTAL &&
12207 ((element_info[element].move_pattern & MV_UP &&
12208 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12209 (element_info[element].move_pattern & MV_DOWN &&
12210 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12211 return MP_NO_ACTION;
12213 /* do not push elements already moving away faster than player */
12214 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12215 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12216 return MP_NO_ACTION;
12218 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12220 if (player->push_delay_value == -1 || !player_was_pushing)
12221 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12223 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12225 if (player->push_delay_value == -1)
12226 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12228 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12230 if (!player->is_pushing)
12231 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12234 player->is_pushing = TRUE;
12235 player->is_active = TRUE;
12237 if (!(IN_LEV_FIELD(nextx, nexty) &&
12238 (IS_FREE(nextx, nexty) ||
12239 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12240 IS_SB_ELEMENT(element)))))
12241 return MP_NO_ACTION;
12243 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12244 return MP_NO_ACTION;
12246 if (player->push_delay == -1) /* new pushing; restart delay */
12247 player->push_delay = 0;
12249 if (player->push_delay < player->push_delay_value &&
12250 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12251 element != EL_SPRING && element != EL_BALLOON)
12253 /* make sure that there is no move delay before next try to push */
12254 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12255 player->move_delay = 0;
12257 return MP_NO_ACTION;
12260 if (IS_SB_ELEMENT(element))
12262 if (element == EL_SOKOBAN_FIELD_FULL)
12264 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12265 local_player->sokobanfields_still_needed++;
12268 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12270 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12271 local_player->sokobanfields_still_needed--;
12274 Feld[x][y] = EL_SOKOBAN_OBJECT;
12276 if (Back[x][y] == Back[nextx][nexty])
12277 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12278 else if (Back[x][y] != 0)
12279 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12282 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12285 if (local_player->sokobanfields_still_needed == 0 &&
12286 game.emulation == EMU_SOKOBAN)
12288 player->LevelSolved = player->GameOver = TRUE;
12289 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12293 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12295 InitMovingField(x, y, move_direction);
12296 GfxAction[x][y] = ACTION_PUSHING;
12298 if (mode == DF_SNAP)
12299 ContinueMoving(x, y);
12301 MovPos[x][y] = (dx != 0 ? dx : dy);
12303 Pushed[x][y] = TRUE;
12304 Pushed[nextx][nexty] = TRUE;
12306 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12307 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12309 player->push_delay_value = -1; /* get new value later */
12311 /* check for element change _after_ element has been pushed */
12312 if (game.use_change_when_pushing_bug)
12314 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12315 player->index_bit, dig_side);
12316 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12317 player->index_bit, dig_side);
12320 else if (IS_SWITCHABLE(element))
12322 if (PLAYER_SWITCHING(player, x, y))
12324 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12325 player->index_bit, dig_side);
12330 player->is_switching = TRUE;
12331 player->switch_x = x;
12332 player->switch_y = y;
12334 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12336 if (element == EL_ROBOT_WHEEL)
12338 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12342 DrawLevelField(x, y);
12344 else if (element == EL_SP_TERMINAL)
12349 SCAN_PLAYFIELD(xx, yy)
12351 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12354 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12356 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12357 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12360 else if (IS_BELT_SWITCH(element))
12362 ToggleBeltSwitch(x, y);
12364 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12365 element == EL_SWITCHGATE_SWITCH_DOWN)
12367 ToggleSwitchgateSwitch(x, y);
12369 else if (element == EL_LIGHT_SWITCH ||
12370 element == EL_LIGHT_SWITCH_ACTIVE)
12372 ToggleLightSwitch(x, y);
12374 else if (element == EL_TIMEGATE_SWITCH)
12376 ActivateTimegateSwitch(x, y);
12378 else if (element == EL_BALLOON_SWITCH_LEFT ||
12379 element == EL_BALLOON_SWITCH_RIGHT ||
12380 element == EL_BALLOON_SWITCH_UP ||
12381 element == EL_BALLOON_SWITCH_DOWN ||
12382 element == EL_BALLOON_SWITCH_NONE ||
12383 element == EL_BALLOON_SWITCH_ANY)
12385 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12386 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12387 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12388 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12389 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12392 else if (element == EL_LAMP)
12394 Feld[x][y] = EL_LAMP_ACTIVE;
12395 local_player->lights_still_needed--;
12397 ResetGfxAnimation(x, y);
12398 DrawLevelField(x, y);
12400 else if (element == EL_TIME_ORB_FULL)
12402 Feld[x][y] = EL_TIME_ORB_EMPTY;
12404 if (level.time > 0 || level.use_time_orb_bug)
12406 TimeLeft += level.time_orb_time;
12407 DrawGameValue_Time(TimeLeft);
12410 ResetGfxAnimation(x, y);
12411 DrawLevelField(x, y);
12413 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12414 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12418 game.ball_state = !game.ball_state;
12421 SCAN_PLAYFIELD(xx, yy)
12423 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12426 int e = Feld[xx][yy];
12428 if (game.ball_state)
12430 if (e == EL_EMC_MAGIC_BALL)
12431 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12432 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12433 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12437 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12438 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12439 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12440 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12445 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12446 player->index_bit, dig_side);
12448 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12449 player->index_bit, dig_side);
12451 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12452 player->index_bit, dig_side);
12458 if (!PLAYER_SWITCHING(player, x, y))
12460 player->is_switching = TRUE;
12461 player->switch_x = x;
12462 player->switch_y = y;
12464 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12465 player->index_bit, dig_side);
12466 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12467 player->index_bit, dig_side);
12469 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12470 player->index_bit, dig_side);
12471 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12472 player->index_bit, dig_side);
12475 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12476 player->index_bit, dig_side);
12477 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12478 player->index_bit, dig_side);
12480 return MP_NO_ACTION;
12483 player->push_delay = -1;
12485 if (is_player) /* function can also be called by EL_PENGUIN */
12487 if (Feld[x][y] != element) /* really digged/collected something */
12489 player->is_collecting = !player->is_digging;
12490 player->is_active = TRUE;
12497 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12499 int jx = player->jx, jy = player->jy;
12500 int x = jx + dx, y = jy + dy;
12501 int snap_direction = (dx == -1 ? MV_LEFT :
12502 dx == +1 ? MV_RIGHT :
12504 dy == +1 ? MV_DOWN : MV_NONE);
12505 boolean can_continue_snapping = (level.continuous_snapping &&
12506 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12508 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12511 if (!player->active || !IN_LEV_FIELD(x, y))
12519 if (player->MovPos == 0)
12520 player->is_pushing = FALSE;
12522 player->is_snapping = FALSE;
12524 if (player->MovPos == 0)
12526 player->is_moving = FALSE;
12527 player->is_digging = FALSE;
12528 player->is_collecting = FALSE;
12534 #if USE_NEW_CONTINUOUS_SNAPPING
12535 /* prevent snapping with already pressed snap key when not allowed */
12536 if (player->is_snapping && !can_continue_snapping)
12539 if (player->is_snapping)
12543 player->MovDir = snap_direction;
12545 if (player->MovPos == 0)
12547 player->is_moving = FALSE;
12548 player->is_digging = FALSE;
12549 player->is_collecting = FALSE;
12552 player->is_dropping = FALSE;
12553 player->is_dropping_pressed = FALSE;
12554 player->drop_pressed_delay = 0;
12556 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12559 player->is_snapping = TRUE;
12560 player->is_active = TRUE;
12562 if (player->MovPos == 0)
12564 player->is_moving = FALSE;
12565 player->is_digging = FALSE;
12566 player->is_collecting = FALSE;
12569 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12570 DrawLevelField(player->last_jx, player->last_jy);
12572 DrawLevelField(x, y);
12577 boolean DropElement(struct PlayerInfo *player)
12579 int old_element, new_element;
12580 int dropx = player->jx, dropy = player->jy;
12581 int drop_direction = player->MovDir;
12582 int drop_side = drop_direction;
12583 int drop_element = (player->inventory_size > 0 ?
12584 player->inventory_element[player->inventory_size - 1] :
12585 player->inventory_infinite_element != EL_UNDEFINED ?
12586 player->inventory_infinite_element :
12587 player->dynabombs_left > 0 ?
12588 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12591 player->is_dropping_pressed = TRUE;
12593 /* do not drop an element on top of another element; when holding drop key
12594 pressed without moving, dropped element must move away before the next
12595 element can be dropped (this is especially important if the next element
12596 is dynamite, which can be placed on background for historical reasons) */
12597 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12600 if (IS_THROWABLE(drop_element))
12602 dropx += GET_DX_FROM_DIR(drop_direction);
12603 dropy += GET_DY_FROM_DIR(drop_direction);
12605 if (!IN_LEV_FIELD(dropx, dropy))
12609 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12610 new_element = drop_element; /* default: no change when dropping */
12612 /* check if player is active, not moving and ready to drop */
12613 if (!player->active || player->MovPos || player->drop_delay > 0)
12616 /* check if player has anything that can be dropped */
12617 if (new_element == EL_UNDEFINED)
12620 /* check if drop key was pressed long enough for EM style dynamite */
12621 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12624 /* check if anything can be dropped at the current position */
12625 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12628 /* collected custom elements can only be dropped on empty fields */
12629 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12632 if (old_element != EL_EMPTY)
12633 Back[dropx][dropy] = old_element; /* store old element on this field */
12635 ResetGfxAnimation(dropx, dropy);
12636 ResetRandomAnimationValue(dropx, dropy);
12638 if (player->inventory_size > 0 ||
12639 player->inventory_infinite_element != EL_UNDEFINED)
12641 if (player->inventory_size > 0)
12643 player->inventory_size--;
12646 DrawGameDoorValues();
12648 DrawGameValue_Dynamite(local_player->inventory_size);
12651 if (new_element == EL_DYNAMITE)
12652 new_element = EL_DYNAMITE_ACTIVE;
12653 else if (new_element == EL_EM_DYNAMITE)
12654 new_element = EL_EM_DYNAMITE_ACTIVE;
12655 else if (new_element == EL_SP_DISK_RED)
12656 new_element = EL_SP_DISK_RED_ACTIVE;
12659 Feld[dropx][dropy] = new_element;
12661 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12662 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12663 el2img(Feld[dropx][dropy]), 0);
12665 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12667 /* needed if previous element just changed to "empty" in the last frame */
12668 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12670 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12671 player->index_bit, drop_side);
12672 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12674 player->index_bit, drop_side);
12676 TestIfElementTouchesCustomElement(dropx, dropy);
12678 else /* player is dropping a dyna bomb */
12680 player->dynabombs_left--;
12682 Feld[dropx][dropy] = new_element;
12684 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12685 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12686 el2img(Feld[dropx][dropy]), 0);
12688 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12691 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12692 InitField_WithBug1(dropx, dropy, FALSE);
12694 new_element = Feld[dropx][dropy]; /* element might have changed */
12696 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12697 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12699 int move_direction, nextx, nexty;
12701 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12702 MovDir[dropx][dropy] = drop_direction;
12704 move_direction = MovDir[dropx][dropy];
12705 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12706 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12708 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12709 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12712 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12713 player->is_dropping = TRUE;
12715 player->drop_pressed_delay = 0;
12716 player->is_dropping_pressed = FALSE;
12718 player->drop_x = dropx;
12719 player->drop_y = dropy;
12724 /* ------------------------------------------------------------------------- */
12725 /* game sound playing functions */
12726 /* ------------------------------------------------------------------------- */
12728 static int *loop_sound_frame = NULL;
12729 static int *loop_sound_volume = NULL;
12731 void InitPlayLevelSound()
12733 int num_sounds = getSoundListSize();
12735 checked_free(loop_sound_frame);
12736 checked_free(loop_sound_volume);
12738 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12739 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12742 static void PlayLevelSound(int x, int y, int nr)
12744 int sx = SCREENX(x), sy = SCREENY(y);
12745 int volume, stereo_position;
12746 int max_distance = 8;
12747 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12749 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12750 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12753 if (!IN_LEV_FIELD(x, y) ||
12754 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12755 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12758 volume = SOUND_MAX_VOLUME;
12760 if (!IN_SCR_FIELD(sx, sy))
12762 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12763 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12765 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12768 stereo_position = (SOUND_MAX_LEFT +
12769 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12770 (SCR_FIELDX + 2 * max_distance));
12772 if (IS_LOOP_SOUND(nr))
12774 /* This assures that quieter loop sounds do not overwrite louder ones,
12775 while restarting sound volume comparison with each new game frame. */
12777 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12780 loop_sound_volume[nr] = volume;
12781 loop_sound_frame[nr] = FrameCounter;
12784 PlaySoundExt(nr, volume, stereo_position, type);
12787 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12789 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12790 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12791 y < LEVELY(BY1) ? LEVELY(BY1) :
12792 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12796 static void PlayLevelSoundAction(int x, int y, int action)
12798 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12801 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12803 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12805 if (sound_effect != SND_UNDEFINED)
12806 PlayLevelSound(x, y, sound_effect);
12809 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12812 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12814 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12815 PlayLevelSound(x, y, sound_effect);
12818 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12820 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12822 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12823 PlayLevelSound(x, y, sound_effect);
12826 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12828 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12830 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12831 StopSound(sound_effect);
12834 static void PlayLevelMusic()
12836 if (levelset.music[level_nr] != MUS_UNDEFINED)
12837 PlayMusic(levelset.music[level_nr]); /* from config file */
12839 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12842 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12844 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12849 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12853 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12857 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12861 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12865 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12869 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12873 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12876 case SAMPLE_android_clone:
12877 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12880 case SAMPLE_android_move:
12881 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12884 case SAMPLE_spring:
12885 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12889 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12893 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12896 case SAMPLE_eater_eat:
12897 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12901 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12904 case SAMPLE_collect:
12905 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12908 case SAMPLE_diamond:
12909 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12912 case SAMPLE_squash:
12913 /* !!! CHECK THIS !!! */
12915 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12917 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12921 case SAMPLE_wonderfall:
12922 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12926 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12930 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12934 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12938 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12942 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12946 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12949 case SAMPLE_wonder:
12950 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12954 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12957 case SAMPLE_exit_open:
12958 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12961 case SAMPLE_exit_leave:
12962 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12965 case SAMPLE_dynamite:
12966 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12970 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12974 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12978 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12982 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12986 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12990 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12994 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12999 void RaiseScore(int value)
13001 local_player->score += value;
13003 DrawGameValue_Score(local_player->score);
13006 void RaiseScoreElement(int element)
13011 case EL_BD_DIAMOND:
13012 case EL_EMERALD_YELLOW:
13013 case EL_EMERALD_RED:
13014 case EL_EMERALD_PURPLE:
13015 case EL_SP_INFOTRON:
13016 RaiseScore(level.score[SC_EMERALD]);
13019 RaiseScore(level.score[SC_DIAMOND]);
13022 RaiseScore(level.score[SC_CRYSTAL]);
13025 RaiseScore(level.score[SC_PEARL]);
13028 case EL_BD_BUTTERFLY:
13029 case EL_SP_ELECTRON:
13030 RaiseScore(level.score[SC_BUG]);
13033 case EL_BD_FIREFLY:
13034 case EL_SP_SNIKSNAK:
13035 RaiseScore(level.score[SC_SPACESHIP]);
13038 case EL_DARK_YAMYAM:
13039 RaiseScore(level.score[SC_YAMYAM]);
13042 RaiseScore(level.score[SC_ROBOT]);
13045 RaiseScore(level.score[SC_PACMAN]);
13048 RaiseScore(level.score[SC_NUT]);
13051 case EL_EM_DYNAMITE:
13052 case EL_SP_DISK_RED:
13053 case EL_DYNABOMB_INCREASE_NUMBER:
13054 case EL_DYNABOMB_INCREASE_SIZE:
13055 case EL_DYNABOMB_INCREASE_POWER:
13056 RaiseScore(level.score[SC_DYNAMITE]);
13058 case EL_SHIELD_NORMAL:
13059 case EL_SHIELD_DEADLY:
13060 RaiseScore(level.score[SC_SHIELD]);
13062 case EL_EXTRA_TIME:
13063 RaiseScore(level.extra_time_score);
13077 RaiseScore(level.score[SC_KEY]);
13080 RaiseScore(element_info[element].collect_score);
13085 void RequestQuitGame(boolean ask_if_really_quit)
13087 if (AllPlayersGone ||
13088 !ask_if_really_quit ||
13089 level_editor_test_game ||
13090 Request("Do you really want to quit the game ?",
13091 REQ_ASK | REQ_STAY_CLOSED))
13093 #if defined(NETWORK_AVALIABLE)
13094 if (options.network)
13095 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13099 game_status = GAME_MODE_MAIN;
13105 if (tape.playing && tape.deactivate_display)
13106 TapeDeactivateDisplayOff(TRUE);
13108 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13110 if (tape.playing && tape.deactivate_display)
13111 TapeDeactivateDisplayOn();
13116 /* ---------- new game button stuff ---------------------------------------- */
13118 /* graphic position values for game buttons */
13119 #define GAME_BUTTON_XSIZE 30
13120 #define GAME_BUTTON_YSIZE 30
13121 #define GAME_BUTTON_XPOS 5
13122 #define GAME_BUTTON_YPOS 215
13123 #define SOUND_BUTTON_XPOS 5
13124 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13126 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13127 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13128 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13129 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13130 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13131 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13138 } gamebutton_info[NUM_GAME_BUTTONS] =
13141 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13146 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13147 GAME_CTRL_ID_PAUSE,
13151 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13156 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13157 SOUND_CTRL_ID_MUSIC,
13158 "background music on/off"
13161 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13162 SOUND_CTRL_ID_LOOPS,
13163 "sound loops on/off"
13166 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13167 SOUND_CTRL_ID_SIMPLE,
13168 "normal sounds on/off"
13172 void CreateGameButtons()
13176 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13178 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13179 struct GadgetInfo *gi;
13182 unsigned long event_mask;
13183 int gd_xoffset, gd_yoffset;
13184 int gd_x1, gd_x2, gd_y1, gd_y2;
13187 gd_xoffset = gamebutton_info[i].x;
13188 gd_yoffset = gamebutton_info[i].y;
13189 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13190 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13192 if (id == GAME_CTRL_ID_STOP ||
13193 id == GAME_CTRL_ID_PAUSE ||
13194 id == GAME_CTRL_ID_PLAY)
13196 button_type = GD_TYPE_NORMAL_BUTTON;
13198 event_mask = GD_EVENT_RELEASED;
13199 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13200 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13204 button_type = GD_TYPE_CHECK_BUTTON;
13206 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13207 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13208 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13209 event_mask = GD_EVENT_PRESSED;
13210 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13211 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13214 gi = CreateGadget(GDI_CUSTOM_ID, id,
13215 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13216 GDI_X, DX + gd_xoffset,
13217 GDI_Y, DY + gd_yoffset,
13218 GDI_WIDTH, GAME_BUTTON_XSIZE,
13219 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13220 GDI_TYPE, button_type,
13221 GDI_STATE, GD_BUTTON_UNPRESSED,
13222 GDI_CHECKED, checked,
13223 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13224 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13225 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13226 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13227 GDI_EVENT_MASK, event_mask,
13228 GDI_CALLBACK_ACTION, HandleGameButtons,
13232 Error(ERR_EXIT, "cannot create gadget");
13234 game_gadget[id] = gi;
13238 void FreeGameButtons()
13242 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13243 FreeGadget(game_gadget[i]);
13246 static void MapGameButtons()
13250 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13251 MapGadget(game_gadget[i]);
13254 void UnmapGameButtons()
13258 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13259 UnmapGadget(game_gadget[i]);
13262 static void HandleGameButtons(struct GadgetInfo *gi)
13264 int id = gi->custom_id;
13266 if (game_status != GAME_MODE_PLAYING)
13271 case GAME_CTRL_ID_STOP:
13275 RequestQuitGame(TRUE);
13278 case GAME_CTRL_ID_PAUSE:
13279 if (options.network)
13281 #if defined(NETWORK_AVALIABLE)
13283 SendToServer_ContinuePlaying();
13285 SendToServer_PausePlaying();
13289 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13292 case GAME_CTRL_ID_PLAY:
13295 #if defined(NETWORK_AVALIABLE)
13296 if (options.network)
13297 SendToServer_ContinuePlaying();
13301 tape.pausing = FALSE;
13302 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13307 case SOUND_CTRL_ID_MUSIC:
13308 if (setup.sound_music)
13310 setup.sound_music = FALSE;
13313 else if (audio.music_available)
13315 setup.sound = setup.sound_music = TRUE;
13317 SetAudioMode(setup.sound);
13323 case SOUND_CTRL_ID_LOOPS:
13324 if (setup.sound_loops)
13325 setup.sound_loops = FALSE;
13326 else if (audio.loops_available)
13328 setup.sound = setup.sound_loops = TRUE;
13329 SetAudioMode(setup.sound);
13333 case SOUND_CTRL_ID_SIMPLE:
13334 if (setup.sound_simple)
13335 setup.sound_simple = FALSE;
13336 else if (audio.sound_available)
13338 setup.sound = setup.sound_simple = TRUE;
13339 SetAudioMode(setup.sound);