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)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
60 /* for MovePlayer() */
61 #define MP_NO_ACTION 0
64 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
66 /* for ScrollPlayer() */
68 #define SCROLL_GO_ON 1
70 /* for Bang()/Explode() */
71 #define EX_PHASE_START 0
72 #define EX_TYPE_NONE 0
73 #define EX_TYPE_NORMAL (1 << 0)
74 #define EX_TYPE_CENTER (1 << 1)
75 #define EX_TYPE_BORDER (1 << 2)
76 #define EX_TYPE_CROSS (1 << 3)
77 #define EX_TYPE_DYNA (1 << 4)
78 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
80 /* special positions in the game control window (relative to control window) */
83 #define XX_EMERALDS 29
84 #define YY_EMERALDS 54
85 #define XX_DYNAMITE 29
86 #define YY_DYNAMITE 89
95 /* special positions in the game control window (relative to main window) */
96 #define DX_LEVEL (DX + XX_LEVEL)
97 #define DY_LEVEL (DY + YY_LEVEL)
98 #define DX_EMERALDS (DX + XX_EMERALDS)
99 #define DY_EMERALDS (DY + YY_EMERALDS)
100 #define DX_DYNAMITE (DX + XX_DYNAMITE)
101 #define DY_DYNAMITE (DY + YY_DYNAMITE)
102 #define DX_KEYS (DX + XX_KEYS)
103 #define DY_KEYS (DY + YY_KEYS)
104 #define DX_SCORE (DX + XX_SCORE)
105 #define DY_SCORE (DY + YY_SCORE)
106 #define DX_TIME1 (DX + XX_TIME1)
107 #define DX_TIME2 (DX + XX_TIME2)
108 #define DY_TIME (DY + YY_TIME)
110 /* values for delayed check of falling and moving elements and for collision */
111 #define CHECK_DELAY_MOVING 3
112 #define CHECK_DELAY_FALLING 3
113 #define CHECK_DELAY_COLLISION 2
115 /* values for initial player move delay (initial delay counter value) */
116 #define INITIAL_MOVE_DELAY_OFF -1
117 #define INITIAL_MOVE_DELAY_ON 0
119 /* values for player movement speed (which is in fact a delay value) */
120 #define MOVE_DELAY_MIN_SPEED 32
121 #define MOVE_DELAY_NORMAL_SPEED 8
122 #define MOVE_DELAY_HIGH_SPEED 4
123 #define MOVE_DELAY_MAX_SPEED 1
126 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
127 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
129 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
130 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
132 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
133 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
135 /* values for other actions */
136 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
137 #define MOVE_STEPSIZE_MIN (1)
138 #define MOVE_STEPSIZE_MAX (TILEX)
140 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
141 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
143 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
145 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
146 RND(element_info[e].push_delay_random))
147 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
148 RND(element_info[e].drop_delay_random))
149 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
150 RND(element_info[e].move_delay_random))
151 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
152 (element_info[e].move_delay_random))
153 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
154 RND(element_info[e].ce_value_random_initial))
155 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
156 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
157 RND((c)->delay_random * (c)->delay_frames))
158 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
159 RND((c)->delay_random))
163 #define GET_VALID_RUNTIME_ELEMENT(e) \
164 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
166 #define GET_VALID_FILE_ELEMENT(e) \
167 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
170 #define GET_TARGET_ELEMENT(e, ch, cv, cs) \
171 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
172 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
173 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
174 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
175 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
176 (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
178 #define CAN_GROW_INTO(e) \
179 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
181 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
182 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
185 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
186 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
187 (CAN_MOVE_INTO_ACID(e) && \
188 Feld[x][y] == EL_ACID) || \
191 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
192 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
193 (CAN_MOVE_INTO_ACID(e) && \
194 Feld[x][y] == EL_ACID) || \
197 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
200 (CAN_MOVE_INTO_ACID(e) && \
201 Feld[x][y] == EL_ACID) || \
202 (DONT_COLLIDE_WITH(e) && \
204 !PLAYER_ENEMY_PROTECTED(x, y))))
206 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
209 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
212 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
215 #define ANDROID_CAN_CLONE_FIELD(x, y) \
216 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
217 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
219 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
220 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
222 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
223 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
225 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
226 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
228 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
229 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
231 #define PIG_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
234 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
236 IS_FOOD_PENGUIN(Feld[x][y])))
237 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
240 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
243 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
244 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
246 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
247 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
248 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
251 #define GROUP_NR(e) ((e) - EL_GROUP_START)
252 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
253 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
255 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
256 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
259 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
261 #define CE_ENTER_FIELD_COND(e, x, y) \
262 (!IS_PLAYER(x, y) && \
263 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
265 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
266 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
268 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
269 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
271 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
272 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
273 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
274 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
276 /* game button identifiers */
277 #define GAME_CTRL_ID_STOP 0
278 #define GAME_CTRL_ID_PAUSE 1
279 #define GAME_CTRL_ID_PLAY 2
280 #define SOUND_CTRL_ID_MUSIC 3
281 #define SOUND_CTRL_ID_LOOPS 4
282 #define SOUND_CTRL_ID_SIMPLE 5
284 #define NUM_GAME_BUTTONS 6
287 /* forward declaration for internal use */
289 static void CreateField(int, int, int);
291 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
292 static void AdvanceFrameAndPlayerCounters(int);
294 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
295 static boolean MovePlayer(struct PlayerInfo *, int, int);
296 static void ScrollPlayer(struct PlayerInfo *, int);
297 static void ScrollScreen(struct PlayerInfo *, int);
299 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
301 static void InitBeltMovement(void);
302 static void CloseAllOpenTimegates(void);
303 static void CheckGravityMovement(struct PlayerInfo *);
304 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
305 static void KillPlayerUnlessEnemyProtected(int, int);
306 static void KillPlayerUnlessExplosionProtected(int, int);
308 static void TestIfPlayerTouchesCustomElement(int, int);
309 static void TestIfElementTouchesCustomElement(int, int);
310 static void TestIfElementHitsCustomElement(int, int, int);
312 static void TestIfElementSmashesCustomElement(int, int, int);
315 static void HandleElementChange(int, int, int);
316 static void ExecuteCustomElementAction(int, int, int, int);
317 static boolean ChangeElement(int, int, int, int);
319 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
320 #define CheckTriggeredElementChange(x, y, e, ev) \
321 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
322 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
323 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
324 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
325 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
326 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
327 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
329 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
330 #define CheckElementChange(x, y, e, te, ev) \
331 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
332 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
333 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
334 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
335 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
337 static void PlayLevelSound(int, int, int);
338 static void PlayLevelSoundNearest(int, int, int);
339 static void PlayLevelSoundAction(int, int, int);
340 static void PlayLevelSoundElementAction(int, int, int, int);
341 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
342 static void PlayLevelSoundActionIfLoop(int, int, int);
343 static void StopLevelSoundActionIfLoop(int, int, int);
344 static void PlayLevelMusic();
346 static void MapGameButtons();
347 static void HandleGameButtons(struct GadgetInfo *);
349 int AmoebeNachbarNr(int, int);
350 void AmoebeUmwandeln(int, int);
351 void ContinueMoving(int, int);
353 void InitMovDir(int, int);
354 void InitAmoebaNr(int, int);
355 int NewHiScore(void);
357 void TestIfGoodThingHitsBadThing(int, int, int);
358 void TestIfBadThingHitsGoodThing(int, int, int);
359 void TestIfPlayerTouchesBadThing(int, int);
360 void TestIfPlayerRunsIntoBadThing(int, int, int);
361 void TestIfBadThingTouchesPlayer(int, int);
362 void TestIfBadThingRunsIntoPlayer(int, int, int);
363 void TestIfFriendTouchesBadThing(int, int);
364 void TestIfBadThingTouchesFriend(int, int);
365 void TestIfBadThingTouchesOtherBadThing(int, int);
367 void KillPlayer(struct PlayerInfo *);
368 void BuryPlayer(struct PlayerInfo *);
369 void RemovePlayer(struct PlayerInfo *);
371 boolean SnapField(struct PlayerInfo *, int, int);
372 boolean DropElement(struct PlayerInfo *);
374 static int getInvisibleActiveFromInvisibleElement(int);
375 static int getInvisibleFromInvisibleActiveElement(int);
377 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
380 /* ------------------------------------------------------------------------- */
381 /* definition of elements that automatically change to other elements after */
382 /* a specified time, eventually calling a function when changing */
383 /* ------------------------------------------------------------------------- */
385 /* forward declaration for changer functions */
386 static void InitBuggyBase(int, int);
387 static void WarnBuggyBase(int, int);
389 static void InitTrap(int, int);
390 static void ActivateTrap(int, int);
391 static void ChangeActiveTrap(int, int);
393 static void InitRobotWheel(int, int);
394 static void RunRobotWheel(int, int);
395 static void StopRobotWheel(int, int);
397 static void InitTimegateWheel(int, int);
398 static void RunTimegateWheel(int, int);
400 static void InitMagicBallDelay(int, int);
401 static void ActivateMagicBall(int, int);
403 static void InitDiagonalMovingElement(int, int);
405 struct ChangingElementInfo
410 void (*pre_change_function)(int x, int y);
411 void (*change_function)(int x, int y);
412 void (*post_change_function)(int x, int y);
415 static struct ChangingElementInfo change_delay_list[] =
466 EL_SWITCHGATE_OPENING,
474 EL_SWITCHGATE_CLOSING,
475 EL_SWITCHGATE_CLOSED,
507 EL_ACID_SPLASH_RIGHT,
516 EL_SP_BUGGY_BASE_ACTIVATING,
523 EL_SP_BUGGY_BASE_ACTIVATING,
524 EL_SP_BUGGY_BASE_ACTIVE,
531 EL_SP_BUGGY_BASE_ACTIVE,
555 EL_ROBOT_WHEEL_ACTIVE,
563 EL_TIMEGATE_SWITCH_ACTIVE,
571 EL_EMC_MAGIC_BALL_ACTIVE,
572 EL_EMC_MAGIC_BALL_ACTIVE,
579 EL_EMC_SPRING_BUMPER_ACTIVE,
580 EL_EMC_SPRING_BUMPER,
587 EL_DIAGONAL_SHRINKING,
600 InitDiagonalMovingElement
616 int push_delay_fixed, push_delay_random;
621 { EL_BALLOON, 0, 0 },
623 { EL_SOKOBAN_OBJECT, 2, 0 },
624 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
625 { EL_SATELLITE, 2, 0 },
626 { EL_SP_DISK_YELLOW, 2, 0 },
628 { EL_UNDEFINED, 0, 0 },
636 move_stepsize_list[] =
638 { EL_AMOEBA_DROP, 2 },
639 { EL_AMOEBA_DROPPING, 2 },
640 { EL_QUICKSAND_FILLING, 1 },
641 { EL_QUICKSAND_EMPTYING, 1 },
642 { EL_MAGIC_WALL_FILLING, 2 },
643 { EL_BD_MAGIC_WALL_FILLING, 2 },
644 { EL_MAGIC_WALL_EMPTYING, 2 },
645 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
655 collect_count_list[] =
658 { EL_BD_DIAMOND, 1 },
659 { EL_EMERALD_YELLOW, 1 },
660 { EL_EMERALD_RED, 1 },
661 { EL_EMERALD_PURPLE, 1 },
663 { EL_SP_INFOTRON, 1 },
675 access_direction_list[] =
677 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
678 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
679 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
680 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
681 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
682 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
683 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
684 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
685 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
686 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
687 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
689 { EL_SP_PORT_LEFT, MV_RIGHT },
690 { EL_SP_PORT_RIGHT, MV_LEFT },
691 { EL_SP_PORT_UP, MV_DOWN },
692 { EL_SP_PORT_DOWN, MV_UP },
693 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
694 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
695 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
696 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
697 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
698 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
699 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
700 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
701 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
702 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
703 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
704 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
705 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
706 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
707 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
709 { EL_UNDEFINED, MV_NONE }
712 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
714 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
715 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
716 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
717 IS_JUST_CHANGING(x, y))
719 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
721 /* static variables for playfield scan mode (scanning forward or backward) */
722 static int playfield_scan_start_x = 0;
723 static int playfield_scan_start_y = 0;
724 static int playfield_scan_delta_x = 1;
725 static int playfield_scan_delta_y = 1;
727 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
728 (y) >= 0 && (y) <= lev_fieldy - 1; \
729 (y) += playfield_scan_delta_y) \
730 for ((x) = playfield_scan_start_x; \
731 (x) >= 0 && (x) <= lev_fieldx - 1; \
732 (x) += playfield_scan_delta_x) \
735 void DEBUG_SetMaximumDynamite()
739 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
740 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
741 local_player->inventory_element[local_player->inventory_size++] =
746 static void InitPlayfieldScanModeVars()
748 if (game.use_reverse_scan_direction)
750 playfield_scan_start_x = lev_fieldx - 1;
751 playfield_scan_start_y = lev_fieldy - 1;
753 playfield_scan_delta_x = -1;
754 playfield_scan_delta_y = -1;
758 playfield_scan_start_x = 0;
759 playfield_scan_start_y = 0;
761 playfield_scan_delta_x = 1;
762 playfield_scan_delta_y = 1;
766 static void InitPlayfieldScanMode(int mode)
768 game.use_reverse_scan_direction =
769 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
771 InitPlayfieldScanModeVars();
774 static int get_move_delay_from_stepsize(int move_stepsize)
777 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
779 /* make sure that stepsize value is always a power of 2 */
780 move_stepsize = (1 << log_2(move_stepsize));
782 return TILEX / move_stepsize;
785 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
788 int player_nr = player->index_nr;
789 int move_delay = get_move_delay_from_stepsize(move_stepsize);
790 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
792 /* do no immediately change move delay -- the player might just be moving */
793 player->move_delay_value_next = move_delay;
795 /* information if player can move must be set separately */
796 player->cannot_move = cannot_move;
800 player->move_delay = game.initial_move_delay[player_nr];
801 player->move_delay_value = game.initial_move_delay_value[player_nr];
803 player->move_delay_value_next = -1;
805 player->move_delay_reset_counter = 0;
809 void GetPlayerConfig()
811 if (!audio.sound_available)
812 setup.sound_simple = FALSE;
814 if (!audio.loops_available)
815 setup.sound_loops = FALSE;
817 if (!audio.music_available)
818 setup.sound_music = FALSE;
820 if (!video.fullscreen_available)
821 setup.fullscreen = FALSE;
823 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
825 SetAudioMode(setup.sound);
829 static int getBeltNrFromBeltElement(int element)
831 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
832 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
833 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
836 static int getBeltNrFromBeltActiveElement(int element)
838 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
839 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
840 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
843 static int getBeltNrFromBeltSwitchElement(int element)
845 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
846 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
847 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
850 static int getBeltDirNrFromBeltSwitchElement(int element)
852 static int belt_base_element[4] =
854 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
855 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
856 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
857 EL_CONVEYOR_BELT_4_SWITCH_LEFT
860 int belt_nr = getBeltNrFromBeltSwitchElement(element);
861 int belt_dir_nr = element - belt_base_element[belt_nr];
863 return (belt_dir_nr % 3);
866 static int getBeltDirFromBeltSwitchElement(int element)
868 static int belt_move_dir[3] =
875 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
877 return belt_move_dir[belt_dir_nr];
880 static int get_element_from_group_element(int element)
882 if (IS_GROUP_ELEMENT(element))
884 struct ElementGroupInfo *group = element_info[element].group;
885 int last_anim_random_frame = gfx.anim_random_frame;
888 if (group->choice_mode == ANIM_RANDOM)
889 gfx.anim_random_frame = RND(group->num_elements_resolved);
891 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
892 group->choice_mode, 0,
895 if (group->choice_mode == ANIM_RANDOM)
896 gfx.anim_random_frame = last_anim_random_frame;
900 element = group->element_resolved[element_pos];
906 static void InitPlayerField(int x, int y, int element, boolean init_game)
908 if (element == EL_SP_MURPHY)
912 if (stored_player[0].present)
914 Feld[x][y] = EL_SP_MURPHY_CLONE;
920 stored_player[0].use_murphy = TRUE;
922 if (!level.use_artwork_element[0])
923 stored_player[0].artwork_element = EL_SP_MURPHY;
926 Feld[x][y] = EL_PLAYER_1;
932 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
933 int jx = player->jx, jy = player->jy;
935 player->present = TRUE;
937 player->block_last_field = (element == EL_SP_MURPHY ?
938 level.sp_block_last_field :
939 level.block_last_field);
941 /* ---------- initialize player's last field block delay --------------- */
943 /* always start with reliable default value (no adjustment needed) */
944 player->block_delay_adjustment = 0;
946 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
947 if (player->block_last_field && element == EL_SP_MURPHY)
948 player->block_delay_adjustment = 1;
950 /* special case 2: in game engines before 3.1.1, blocking was different */
951 if (game.use_block_last_field_bug)
952 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
954 if (!options.network || player->connected)
956 player->active = TRUE;
958 /* remove potentially duplicate players */
959 if (StorePlayer[jx][jy] == Feld[x][y])
960 StorePlayer[jx][jy] = 0;
962 StorePlayer[x][y] = Feld[x][y];
966 printf("Player %d activated.\n", player->element_nr);
967 printf("[Local player is %d and currently %s.]\n",
968 local_player->element_nr,
969 local_player->active ? "active" : "not active");
973 Feld[x][y] = EL_EMPTY;
975 player->jx = player->last_jx = x;
976 player->jy = player->last_jy = y;
980 static void InitField(int x, int y, boolean init_game)
982 int element = Feld[x][y];
991 InitPlayerField(x, y, element, init_game);
994 case EL_SOKOBAN_FIELD_PLAYER:
995 element = Feld[x][y] = EL_PLAYER_1;
996 InitField(x, y, init_game);
998 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
999 InitField(x, y, init_game);
1002 case EL_SOKOBAN_FIELD_EMPTY:
1003 local_player->sokobanfields_still_needed++;
1007 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1008 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1009 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1010 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1011 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1012 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1013 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1014 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1015 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1016 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1025 case EL_SPACESHIP_RIGHT:
1026 case EL_SPACESHIP_UP:
1027 case EL_SPACESHIP_LEFT:
1028 case EL_SPACESHIP_DOWN:
1029 case EL_BD_BUTTERFLY:
1030 case EL_BD_BUTTERFLY_RIGHT:
1031 case EL_BD_BUTTERFLY_UP:
1032 case EL_BD_BUTTERFLY_LEFT:
1033 case EL_BD_BUTTERFLY_DOWN:
1035 case EL_BD_FIREFLY_RIGHT:
1036 case EL_BD_FIREFLY_UP:
1037 case EL_BD_FIREFLY_LEFT:
1038 case EL_BD_FIREFLY_DOWN:
1039 case EL_PACMAN_RIGHT:
1041 case EL_PACMAN_LEFT:
1042 case EL_PACMAN_DOWN:
1044 case EL_YAMYAM_LEFT:
1045 case EL_YAMYAM_RIGHT:
1047 case EL_YAMYAM_DOWN:
1048 case EL_DARK_YAMYAM:
1051 case EL_SP_SNIKSNAK:
1052 case EL_SP_ELECTRON:
1061 case EL_AMOEBA_FULL:
1066 case EL_AMOEBA_DROP:
1067 if (y == lev_fieldy - 1)
1069 Feld[x][y] = EL_AMOEBA_GROWING;
1070 Store[x][y] = EL_AMOEBA_WET;
1074 case EL_DYNAMITE_ACTIVE:
1075 case EL_SP_DISK_RED_ACTIVE:
1076 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1077 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1078 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1079 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1080 MovDelay[x][y] = 96;
1083 case EL_EM_DYNAMITE_ACTIVE:
1084 MovDelay[x][y] = 32;
1088 local_player->lights_still_needed++;
1092 local_player->friends_still_needed++;
1097 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1100 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1101 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1102 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1103 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1104 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1105 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1106 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1107 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1108 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1109 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1110 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1111 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1114 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1115 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1116 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1118 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1120 game.belt_dir[belt_nr] = belt_dir;
1121 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1123 else /* more than one switch -- set it like the first switch */
1125 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1130 #if !USE_BOTH_SWITCHGATE_SWITCHES
1131 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1133 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1137 case EL_LIGHT_SWITCH_ACTIVE:
1139 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1142 case EL_INVISIBLE_STEELWALL:
1143 case EL_INVISIBLE_WALL:
1144 case EL_INVISIBLE_SAND:
1145 if (game.light_time_left > 0 ||
1146 game.lenses_time_left > 0)
1147 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1150 case EL_EMC_MAGIC_BALL:
1151 if (game.ball_state)
1152 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1155 case EL_EMC_MAGIC_BALL_SWITCH:
1156 if (game.ball_state)
1157 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1162 if (IS_CUSTOM_ELEMENT(element))
1164 if (CAN_MOVE(element))
1167 #if USE_NEW_CUSTOM_VALUE
1168 if (!element_info[element].use_last_ce_value || init_game)
1169 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1173 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1176 else if (IS_GROUP_ELEMENT(element))
1179 Feld[x][y] = get_element_from_group_element(element);
1181 InitField(x, y, init_game);
1183 struct ElementGroupInfo *group = element_info[element].group;
1184 int last_anim_random_frame = gfx.anim_random_frame;
1187 if (group->choice_mode == ANIM_RANDOM)
1188 gfx.anim_random_frame = RND(group->num_elements_resolved);
1190 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1191 group->choice_mode, 0,
1194 if (group->choice_mode == ANIM_RANDOM)
1195 gfx.anim_random_frame = last_anim_random_frame;
1197 group->choice_pos++;
1199 Feld[x][y] = group->element_resolved[element_pos];
1201 InitField(x, y, init_game);
1209 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1214 #if USE_NEW_CUSTOM_VALUE
1217 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1219 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1227 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1229 InitField(x, y, init_game);
1231 /* not needed to call InitMovDir() -- already done by InitField()! */
1232 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1233 CAN_MOVE(Feld[x][y]))
1237 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1239 int old_element = Feld[x][y];
1241 InitField(x, y, init_game);
1243 /* not needed to call InitMovDir() -- already done by InitField()! */
1244 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1245 CAN_MOVE(old_element) &&
1246 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1249 /* this case is in fact a combination of not less than three bugs:
1250 first, it calls InitMovDir() for elements that can move, although this is
1251 already done by InitField(); then, it checks the element that was at this
1252 field _before_ the call to InitField() (which can change it); lastly, it
1253 was not called for "mole with direction" elements, which were treated as
1254 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1258 inline void DrawGameValue_Emeralds(int value)
1260 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1262 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1265 inline void DrawGameValue_Dynamite(int value)
1267 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1269 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1272 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1274 int base_key_graphic = EL_KEY_1;
1277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1278 base_key_graphic = EL_EM_KEY_1;
1280 /* currently only 4 of 8 possible keys are displayed */
1281 for (i = 0; i < STD_NUM_KEYS; i++)
1284 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1285 el2edimg(base_key_graphic + i));
1287 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1288 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1289 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1293 inline void DrawGameValue_Score(int value)
1295 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1297 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1300 inline void DrawGameValue_Time(int value)
1302 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1303 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1305 /* clear background if value just changed its size */
1306 if (value == 999 || value == 1000)
1307 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1310 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1312 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1315 inline void DrawGameValue_Level(int value)
1318 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1321 /* misuse area for displaying emeralds to draw bigger level number */
1322 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1323 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1325 /* now copy it to the area for displaying level number */
1326 BlitBitmap(drawto, drawto,
1327 DX_EMERALDS, DY_EMERALDS + 1,
1328 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1329 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1330 DX_LEVEL - 1, DY_LEVEL + 1);
1332 /* restore the area for displaying emeralds */
1333 DrawGameValue_Emeralds(local_player->gems_still_needed);
1335 /* yes, this is all really ugly :-) */
1339 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1342 int key[MAX_NUM_KEYS];
1345 for (i = 0; i < MAX_NUM_KEYS; i++)
1346 key[i] = key_bits & (1 << i);
1348 DrawGameValue_Level(level_nr);
1350 DrawGameValue_Emeralds(emeralds);
1351 DrawGameValue_Dynamite(dynamite);
1352 DrawGameValue_Score(score);
1353 DrawGameValue_Time(time);
1355 DrawGameValue_Keys(key);
1358 void DrawGameDoorValues()
1360 int dynamite_state = 0;
1364 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1366 DrawGameDoorValues_EM();
1372 DrawGameValue_Level(level_nr);
1374 DrawGameValue_Emeralds(local_player->gems_still_needed);
1375 DrawGameValue_Dynamite(local_player->inventory_size);
1376 DrawGameValue_Score(local_player->score);
1377 DrawGameValue_Time(TimeLeft);
1381 if (game.centered_player_nr == -1)
1383 for (i = 0; i < MAX_PLAYERS; i++)
1385 for (j = 0; j < MAX_NUM_KEYS; j++)
1386 if (stored_player[i].key[j])
1387 key_bits |= (1 << j);
1389 dynamite_state += stored_player[i].inventory_size;
1393 DrawGameValue_Keys(stored_player[i].key);
1398 int player_nr = game.centered_player_nr;
1400 for (i = 0; i < MAX_NUM_KEYS; i++)
1401 if (stored_player[player_nr].key[i])
1402 key_bits |= (1 << i);
1404 dynamite_state = stored_player[player_nr].inventory_size;
1407 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1408 local_player->score, TimeLeft, key_bits);
1413 static void resolve_group_element(int group_element, int recursion_depth)
1415 static int group_nr;
1416 static struct ElementGroupInfo *group;
1417 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1420 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1422 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1423 group_element - EL_GROUP_START + 1);
1425 /* replace element which caused too deep recursion by question mark */
1426 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1431 if (recursion_depth == 0) /* initialization */
1433 group = element_info[group_element].group;
1434 group_nr = group_element - EL_GROUP_START;
1436 group->num_elements_resolved = 0;
1437 group->choice_pos = 0;
1440 for (i = 0; i < actual_group->num_elements; i++)
1442 int element = actual_group->element[i];
1444 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1447 if (IS_GROUP_ELEMENT(element))
1448 resolve_group_element(element, recursion_depth + 1);
1451 group->element_resolved[group->num_elements_resolved++] = element;
1452 element_info[element].in_group[group_nr] = TRUE;
1459 =============================================================================
1461 -----------------------------------------------------------------------------
1462 initialize game engine due to level / tape version number
1463 =============================================================================
1466 static void InitGameEngine()
1468 int i, j, k, l, x, y;
1470 /* set game engine from tape file when re-playing, else from level file */
1471 game.engine_version = (tape.playing ? tape.engine_version :
1472 level.game_version);
1474 /* ---------------------------------------------------------------------- */
1475 /* set flags for bugs and changes according to active game engine version */
1476 /* ---------------------------------------------------------------------- */
1479 Summary of bugfix/change:
1480 Fixed handling for custom elements that change when pushed by the player.
1482 Fixed/changed in version:
1486 Before 3.1.0, custom elements that "change when pushing" changed directly
1487 after the player started pushing them (until then handled in "DigField()").
1488 Since 3.1.0, these custom elements are not changed until the "pushing"
1489 move of the element is finished (now handled in "ContinueMoving()").
1491 Affected levels/tapes:
1492 The first condition is generally needed for all levels/tapes before version
1493 3.1.0, which might use the old behaviour before it was changed; known tapes
1494 that are affected are some tapes from the level set "Walpurgis Gardens" by
1496 The second condition is an exception from the above case and is needed for
1497 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1498 above (including some development versions of 3.1.0), but before it was
1499 known that this change would break tapes like the above and was fixed in
1500 3.1.1, so that the changed behaviour was active although the engine version
1501 while recording maybe was before 3.1.0. There is at least one tape that is
1502 affected by this exception, which is the tape for the one-level set "Bug
1503 Machine" by Juergen Bonhagen.
1506 game.use_change_when_pushing_bug =
1507 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1509 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1510 tape.game_version < VERSION_IDENT(3,1,1,0)));
1513 Summary of bugfix/change:
1514 Fixed handling for blocking the field the player leaves when moving.
1516 Fixed/changed in version:
1520 Before 3.1.1, when "block last field when moving" was enabled, the field
1521 the player is leaving when moving was blocked for the time of the move,
1522 and was directly unblocked afterwards. This resulted in the last field
1523 being blocked for exactly one less than the number of frames of one player
1524 move. Additionally, even when blocking was disabled, the last field was
1525 blocked for exactly one frame.
1526 Since 3.1.1, due to changes in player movement handling, the last field
1527 is not blocked at all when blocking is disabled. When blocking is enabled,
1528 the last field is blocked for exactly the number of frames of one player
1529 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1530 last field is blocked for exactly one more than the number of frames of
1533 Affected levels/tapes:
1534 (!!! yet to be determined -- probably many !!!)
1537 game.use_block_last_field_bug =
1538 (game.engine_version < VERSION_IDENT(3,1,1,0));
1541 Summary of bugfix/change:
1542 Changed behaviour of CE changes with multiple changes per single frame.
1544 Fixed/changed in version:
1548 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1549 This resulted in race conditions where CEs seem to behave strange in some
1550 situations (where triggered CE changes were just skipped because there was
1551 already a CE change on that tile in the playfield in that engine frame).
1552 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1553 (The number of changes per frame must be limited in any case, because else
1554 it is easily possible to define CE changes that would result in an infinite
1555 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1556 should be set large enough so that it would only be reached in cases where
1557 the corresponding CE change conditions run into a loop. Therefore, it seems
1558 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1559 maximal number of change pages for custom elements.)
1561 Affected levels/tapes:
1565 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1566 game.max_num_changes_per_frame = 1;
1568 game.max_num_changes_per_frame =
1569 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1572 /* ---------------------------------------------------------------------- */
1574 /* default scan direction: scan playfield from top/left to bottom/right */
1575 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1577 /* dynamically adjust element properties according to game engine version */
1578 InitElementPropertiesEngine(game.engine_version);
1581 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1582 printf(" tape version == %06d [%s] [file: %06d]\n",
1583 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1585 printf(" => game.engine_version == %06d\n", game.engine_version);
1589 /* ---------- recursively resolve group elements ------------------------- */
1591 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1592 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1593 element_info[i].in_group[j] = FALSE;
1595 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1596 resolve_group_element(EL_GROUP_START + i, 0);
1599 /* ---------- initialize player's initial move delay --------------------- */
1602 /* dynamically adjust player properties according to level information */
1603 for (i = 0; i < MAX_PLAYERS; i++)
1604 game.initial_move_delay_value[i] =
1605 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1607 /* dynamically adjust player properties according to level information */
1608 game.initial_move_delay_value =
1609 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1612 /* dynamically adjust player properties according to game engine version */
1613 for (i = 0; i < MAX_PLAYERS; i++)
1614 game.initial_move_delay[i] =
1615 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1616 game.initial_move_delay_value[i] : 0);
1618 /* ---------- initialize player's initial push delay --------------------- */
1620 /* dynamically adjust player properties according to game engine version */
1621 game.initial_push_delay_value =
1622 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1624 /* ---------- initialize changing elements ------------------------------- */
1626 /* initialize changing elements information */
1627 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1629 struct ElementInfo *ei = &element_info[i];
1631 /* this pointer might have been changed in the level editor */
1632 ei->change = &ei->change_page[0];
1634 if (!IS_CUSTOM_ELEMENT(i))
1636 ei->change->target_element = EL_EMPTY_SPACE;
1637 ei->change->delay_fixed = 0;
1638 ei->change->delay_random = 0;
1639 ei->change->delay_frames = 1;
1642 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1644 ei->has_change_event[j] = FALSE;
1646 ei->event_page_nr[j] = 0;
1647 ei->event_page[j] = &ei->change_page[0];
1651 /* add changing elements from pre-defined list */
1652 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1654 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1655 struct ElementInfo *ei = &element_info[ch_delay->element];
1657 ei->change->target_element = ch_delay->target_element;
1658 ei->change->delay_fixed = ch_delay->change_delay;
1660 ei->change->pre_change_function = ch_delay->pre_change_function;
1661 ei->change->change_function = ch_delay->change_function;
1662 ei->change->post_change_function = ch_delay->post_change_function;
1664 ei->change->can_change = TRUE;
1665 ei->change->can_change_or_has_action = TRUE;
1667 ei->has_change_event[CE_DELAY] = TRUE;
1669 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1670 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1673 /* ---------- initialize internal run-time variables ------------- */
1675 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1677 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1679 for (j = 0; j < ei->num_change_pages; j++)
1681 ei->change_page[j].can_change_or_has_action =
1682 (ei->change_page[j].can_change |
1683 ei->change_page[j].has_action);
1687 /* add change events from custom element configuration */
1688 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1690 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1692 for (j = 0; j < ei->num_change_pages; j++)
1694 if (!ei->change_page[j].can_change_or_has_action)
1697 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1699 /* only add event page for the first page found with this event */
1700 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1702 ei->has_change_event[k] = TRUE;
1704 ei->event_page_nr[k] = j;
1705 ei->event_page[k] = &ei->change_page[j];
1711 /* ---------- initialize run-time trigger player and element ------------- */
1713 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1715 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1717 for (j = 0; j < ei->num_change_pages; j++)
1719 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1720 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1721 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1722 ei->change_page[j].actual_trigger_ce_value = 0;
1723 ei->change_page[j].actual_trigger_ce_score = 0;
1727 /* ---------- initialize trigger events ---------------------------------- */
1729 /* initialize trigger events information */
1730 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1731 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1732 trigger_events[i][j] = FALSE;
1734 /* add trigger events from element change event properties */
1735 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1737 struct ElementInfo *ei = &element_info[i];
1739 for (j = 0; j < ei->num_change_pages; j++)
1741 if (!ei->change_page[j].can_change_or_has_action)
1744 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1746 int trigger_element = ei->change_page[j].trigger_element;
1748 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1750 if (ei->change_page[j].has_event[k])
1752 if (IS_GROUP_ELEMENT(trigger_element))
1754 struct ElementGroupInfo *group =
1755 element_info[trigger_element].group;
1757 for (l = 0; l < group->num_elements_resolved; l++)
1758 trigger_events[group->element_resolved[l]][k] = TRUE;
1761 trigger_events[trigger_element][k] = TRUE;
1768 /* ---------- initialize push delay -------------------------------------- */
1770 /* initialize push delay values to default */
1771 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1773 if (!IS_CUSTOM_ELEMENT(i))
1776 /* set default push delay values (corrected since version 3.0.7-1) */
1777 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1779 element_info[i].push_delay_fixed = 2;
1780 element_info[i].push_delay_random = 8;
1784 element_info[i].push_delay_fixed = 8;
1785 element_info[i].push_delay_random = 8;
1788 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1789 element_info[i].push_delay_random = game.default_push_delay_random;
1794 /* set push delay value for certain elements from pre-defined list */
1795 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1797 int e = push_delay_list[i].element;
1799 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1800 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1803 /* set push delay value for Supaplex elements for newer engine versions */
1804 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1806 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1808 if (IS_SP_ELEMENT(i))
1810 /* set SP push delay to just enough to push under a falling zonk */
1811 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1813 element_info[i].push_delay_fixed = delay;
1814 element_info[i].push_delay_random = 0;
1819 /* ---------- initialize move stepsize ----------------------------------- */
1821 /* initialize move stepsize values to default */
1822 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1823 if (!IS_CUSTOM_ELEMENT(i))
1824 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1826 /* set move stepsize value for certain elements from pre-defined list */
1827 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1829 int e = move_stepsize_list[i].element;
1831 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1834 /* ---------- initialize collect score ----------------------------------- */
1836 /* initialize collect score values for custom elements from initial value */
1837 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1838 if (IS_CUSTOM_ELEMENT(i))
1839 element_info[i].collect_score = element_info[i].collect_score_initial;
1841 /* ---------- initialize collect count ----------------------------------- */
1843 /* initialize collect count values for non-custom elements */
1844 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1845 if (!IS_CUSTOM_ELEMENT(i))
1846 element_info[i].collect_count_initial = 0;
1848 /* add collect count values for all elements from pre-defined list */
1849 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1850 element_info[collect_count_list[i].element].collect_count_initial =
1851 collect_count_list[i].count;
1853 /* ---------- initialize access direction -------------------------------- */
1855 /* initialize access direction values to default (access from every side) */
1856 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1857 if (!IS_CUSTOM_ELEMENT(i))
1858 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1860 /* set access direction value for certain elements from pre-defined list */
1861 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1862 element_info[access_direction_list[i].element].access_direction =
1863 access_direction_list[i].direction;
1865 /* ---------- initialize explosion content ------------------------------- */
1866 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1868 if (IS_CUSTOM_ELEMENT(i))
1871 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1873 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1875 element_info[i].content.e[x][y] =
1876 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1877 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1878 i == EL_PLAYER_3 ? EL_EMERALD :
1879 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1880 i == EL_MOLE ? EL_EMERALD_RED :
1881 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1882 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1883 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1884 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1885 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1886 i == EL_WALL_EMERALD ? EL_EMERALD :
1887 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1888 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1889 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1890 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1891 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1892 i == EL_WALL_PEARL ? EL_PEARL :
1893 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1899 int get_num_special_action(int element, int action_first, int action_last)
1901 int num_special_action = 0;
1904 for (i = action_first; i <= action_last; i++)
1906 boolean found = FALSE;
1908 for (j = 0; j < NUM_DIRECTIONS; j++)
1909 if (el_act_dir2img(element, i, j) !=
1910 el_act_dir2img(element, ACTION_DEFAULT, j))
1914 num_special_action++;
1920 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1923 return num_special_action;
1927 =============================================================================
1929 -----------------------------------------------------------------------------
1930 initialize and start new game
1931 =============================================================================
1936 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1937 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1938 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1943 /* don't play tapes over network */
1944 network_playing = (options.network && !tape.playing);
1946 for (i = 0; i < MAX_PLAYERS; i++)
1948 struct PlayerInfo *player = &stored_player[i];
1950 player->index_nr = i;
1951 player->index_bit = (1 << i);
1952 player->element_nr = EL_PLAYER_1 + i;
1954 player->present = FALSE;
1955 player->active = FALSE;
1958 player->effective_action = 0;
1959 player->programmed_action = 0;
1962 player->gems_still_needed = level.gems_needed;
1963 player->sokobanfields_still_needed = 0;
1964 player->lights_still_needed = 0;
1965 player->friends_still_needed = 0;
1967 for (j = 0; j < MAX_NUM_KEYS; j++)
1968 player->key[j] = FALSE;
1970 player->dynabomb_count = 0;
1971 player->dynabomb_size = 1;
1972 player->dynabombs_left = 0;
1973 player->dynabomb_xl = FALSE;
1975 player->MovDir = MV_NONE;
1978 player->GfxDir = MV_NONE;
1979 player->GfxAction = ACTION_DEFAULT;
1981 player->StepFrame = 0;
1983 player->use_murphy = FALSE;
1984 player->artwork_element =
1985 (level.use_artwork_element[i] ? level.artwork_element[i] :
1986 player->element_nr);
1988 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1989 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1991 player->gravity = level.initial_player_gravity[i];
1993 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1995 player->actual_frame_counter = 0;
1997 player->step_counter = 0;
1999 player->last_move_dir = MV_NONE;
2001 player->is_active = FALSE;
2003 player->is_waiting = FALSE;
2004 player->is_moving = FALSE;
2005 player->is_auto_moving = FALSE;
2006 player->is_digging = FALSE;
2007 player->is_snapping = FALSE;
2008 player->is_collecting = FALSE;
2009 player->is_pushing = FALSE;
2010 player->is_switching = FALSE;
2011 player->is_dropping = FALSE;
2012 player->is_dropping_pressed = FALSE;
2014 player->is_bored = FALSE;
2015 player->is_sleeping = FALSE;
2017 player->frame_counter_bored = -1;
2018 player->frame_counter_sleeping = -1;
2020 player->anim_delay_counter = 0;
2021 player->post_delay_counter = 0;
2023 player->dir_waiting = MV_NONE;
2024 player->action_waiting = ACTION_DEFAULT;
2025 player->last_action_waiting = ACTION_DEFAULT;
2026 player->special_action_bored = ACTION_DEFAULT;
2027 player->special_action_sleeping = ACTION_DEFAULT;
2030 /* cannot be set here -- could be modified in Init[Player]Field() below */
2032 /* set number of special actions for bored and sleeping animation */
2033 player->num_special_action_bored =
2034 get_num_special_action(player->artwork_element,
2035 ACTION_BORING_1, ACTION_BORING_LAST);
2036 player->num_special_action_sleeping =
2037 get_num_special_action(player->artwork_element,
2038 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2041 player->switch_x = -1;
2042 player->switch_y = -1;
2044 player->drop_x = -1;
2045 player->drop_y = -1;
2047 player->show_envelope = 0;
2050 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2052 player->move_delay = game.initial_move_delay;
2053 player->move_delay_value = game.initial_move_delay_value;
2055 player->move_delay_value_next = -1;
2057 player->move_delay_reset_counter = 0;
2059 player->cannot_move = FALSE;
2062 player->push_delay = -1; /* initialized when pushing starts */
2063 player->push_delay_value = game.initial_push_delay_value;
2065 player->drop_delay = 0;
2066 player->drop_pressed_delay = 0;
2068 player->last_jx = player->last_jy = 0;
2069 player->jx = player->jy = 0;
2071 player->shield_normal_time_left = 0;
2072 player->shield_deadly_time_left = 0;
2074 player->inventory_infinite_element = EL_UNDEFINED;
2075 player->inventory_size = 0;
2077 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2078 SnapField(player, 0, 0);
2080 player->LevelSolved = FALSE;
2081 player->GameOver = FALSE;
2084 network_player_action_received = FALSE;
2086 #if defined(NETWORK_AVALIABLE)
2087 /* initial null action */
2088 if (network_playing)
2089 SendToServer_MovePlayer(MV_NONE);
2098 TimeLeft = level.time;
2101 ScreenMovDir = MV_NONE;
2105 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2107 AllPlayersGone = FALSE;
2109 game.yamyam_content_nr = 0;
2110 game.magic_wall_active = FALSE;
2111 game.magic_wall_time_left = 0;
2112 game.light_time_left = 0;
2113 game.timegate_time_left = 0;
2114 game.switchgate_pos = 0;
2115 game.wind_direction = level.wind_direction_initial;
2117 #if !USE_PLAYER_GRAVITY
2119 game.gravity = FALSE;
2121 game.gravity = level.initial_gravity;
2123 game.explosions_delayed = TRUE;
2126 game.lenses_time_left = 0;
2127 game.magnify_time_left = 0;
2129 game.ball_state = level.ball_state_initial;
2130 game.ball_content_nr = 0;
2132 game.envelope_active = FALSE;
2134 /* set focus to local player for network games, else to all players */
2135 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2136 game.centered_player_nr_next = game.centered_player_nr;
2137 game.set_centered_player = FALSE;
2139 if (network_playing && tape.recording)
2141 /* store client dependent player focus when recording network games */
2142 tape.centered_player_nr_next = game.centered_player_nr_next;
2143 tape.set_centered_player = TRUE;
2147 printf("::: focus set to player %d [%d]\n",
2148 game.centered_player_nr, local_player->index_nr);
2151 for (i = 0; i < NUM_BELTS; i++)
2153 game.belt_dir[i] = MV_NONE;
2154 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2157 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2158 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2161 SCAN_PLAYFIELD(x, y)
2163 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2166 Feld[x][y] = level.field[x][y];
2167 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2168 ChangeDelay[x][y] = 0;
2169 ChangePage[x][y] = -1;
2170 #if USE_NEW_CUSTOM_VALUE
2171 CustomValue[x][y] = 0; /* initialized in InitField() */
2173 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2175 WasJustMoving[x][y] = 0;
2176 WasJustFalling[x][y] = 0;
2177 CheckCollision[x][y] = 0;
2179 Pushed[x][y] = FALSE;
2181 ChangeCount[x][y] = 0;
2182 ChangeEvent[x][y] = -1;
2184 ExplodePhase[x][y] = 0;
2185 ExplodeDelay[x][y] = 0;
2186 ExplodeField[x][y] = EX_TYPE_NONE;
2188 RunnerVisit[x][y] = 0;
2189 PlayerVisit[x][y] = 0;
2192 GfxRandom[x][y] = INIT_GFX_RANDOM();
2193 GfxElement[x][y] = EL_UNDEFINED;
2194 GfxAction[x][y] = ACTION_DEFAULT;
2195 GfxDir[x][y] = MV_NONE;
2199 SCAN_PLAYFIELD(x, y)
2201 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2204 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2206 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2208 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2211 InitField(x, y, TRUE);
2216 for (i = 0; i < MAX_PLAYERS; i++)
2218 struct PlayerInfo *player = &stored_player[i];
2221 /* set number of special actions for bored and sleeping animation */
2222 player->num_special_action_bored =
2223 get_num_special_action(player->artwork_element,
2224 ACTION_BORING_1, ACTION_BORING_LAST);
2225 player->num_special_action_sleeping =
2226 get_num_special_action(player->artwork_element,
2227 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2232 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2233 emulate_sb ? EMU_SOKOBAN :
2234 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2236 #if USE_NEW_ALL_SLIPPERY
2237 /* initialize type of slippery elements */
2238 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2240 if (!IS_CUSTOM_ELEMENT(i))
2242 /* default: elements slip down either to the left or right randomly */
2243 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2245 /* SP style elements prefer to slip down on the left side */
2246 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2247 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2249 /* BD style elements prefer to slip down on the left side */
2250 if (game.emulation == EMU_BOULDERDASH)
2251 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2256 /* initialize explosion and ignition delay */
2257 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2259 if (!IS_CUSTOM_ELEMENT(i))
2262 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2263 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2264 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2265 int last_phase = (num_phase + 1) * delay;
2266 int half_phase = (num_phase / 2) * delay;
2268 element_info[i].explosion_delay = last_phase - 1;
2269 element_info[i].ignition_delay = half_phase;
2271 if (i == EL_BLACK_ORB)
2272 element_info[i].ignition_delay = 1;
2276 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2277 element_info[i].explosion_delay = 1;
2279 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2280 element_info[i].ignition_delay = 1;
2284 /* correct non-moving belts to start moving left */
2285 for (i = 0; i < NUM_BELTS; i++)
2286 if (game.belt_dir[i] == MV_NONE)
2287 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2289 /* check if any connected player was not found in playfield */
2290 for (i = 0; i < MAX_PLAYERS; i++)
2292 struct PlayerInfo *player = &stored_player[i];
2294 if (player->connected && !player->present)
2296 for (j = 0; j < MAX_PLAYERS; j++)
2298 struct PlayerInfo *some_player = &stored_player[j];
2299 int jx = some_player->jx, jy = some_player->jy;
2301 /* assign first free player found that is present in the playfield */
2302 if (some_player->present && !some_player->connected)
2304 player->present = TRUE;
2305 player->active = TRUE;
2307 some_player->present = FALSE;
2308 some_player->active = FALSE;
2311 player->element_nr = some_player->element_nr;
2314 player->artwork_element = some_player->artwork_element;
2316 player->block_last_field = some_player->block_last_field;
2317 player->block_delay_adjustment = some_player->block_delay_adjustment;
2319 StorePlayer[jx][jy] = player->element_nr;
2320 player->jx = player->last_jx = jx;
2321 player->jy = player->last_jy = jy;
2331 /* when playing a tape, eliminate all players who do not participate */
2333 for (i = 0; i < MAX_PLAYERS; i++)
2335 if (stored_player[i].active && !tape.player_participates[i])
2337 struct PlayerInfo *player = &stored_player[i];
2338 int jx = player->jx, jy = player->jy;
2340 player->active = FALSE;
2341 StorePlayer[jx][jy] = 0;
2342 Feld[jx][jy] = EL_EMPTY;
2346 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2348 /* when in single player mode, eliminate all but the first active player */
2350 for (i = 0; i < MAX_PLAYERS; i++)
2352 if (stored_player[i].active)
2354 for (j = i + 1; j < MAX_PLAYERS; j++)
2356 if (stored_player[j].active)
2358 struct PlayerInfo *player = &stored_player[j];
2359 int jx = player->jx, jy = player->jy;
2361 player->active = FALSE;
2362 player->present = FALSE;
2364 StorePlayer[jx][jy] = 0;
2365 Feld[jx][jy] = EL_EMPTY;
2372 /* when recording the game, store which players take part in the game */
2375 for (i = 0; i < MAX_PLAYERS; i++)
2376 if (stored_player[i].active)
2377 tape.player_participates[i] = TRUE;
2382 for (i = 0; i < MAX_PLAYERS; i++)
2384 struct PlayerInfo *player = &stored_player[i];
2386 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2391 if (local_player == player)
2392 printf("Player %d is local player.\n", i+1);
2396 if (BorderElement == EL_EMPTY)
2399 SBX_Right = lev_fieldx - SCR_FIELDX;
2401 SBY_Lower = lev_fieldy - SCR_FIELDY;
2406 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2408 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2411 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2412 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2414 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2415 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2417 /* if local player not found, look for custom element that might create
2418 the player (make some assumptions about the right custom element) */
2419 if (!local_player->present)
2421 int start_x = 0, start_y = 0;
2422 int found_rating = 0;
2423 int found_element = EL_UNDEFINED;
2424 int player_nr = local_player->index_nr;
2427 SCAN_PLAYFIELD(x, y)
2429 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2432 int element = Feld[x][y];
2437 if (level.use_start_element[player_nr] &&
2438 level.start_element[player_nr] == element &&
2445 found_element = element;
2448 if (!IS_CUSTOM_ELEMENT(element))
2451 if (CAN_CHANGE(element))
2453 for (i = 0; i < element_info[element].num_change_pages; i++)
2455 /* check for player created from custom element as single target */
2456 content = element_info[element].change_page[i].target_element;
2457 is_player = ELEM_IS_PLAYER(content);
2459 if (is_player && (found_rating < 3 || element < found_element))
2465 found_element = element;
2470 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2472 /* check for player created from custom element as explosion content */
2473 content = element_info[element].content.e[xx][yy];
2474 is_player = ELEM_IS_PLAYER(content);
2476 if (is_player && (found_rating < 2 || element < found_element))
2478 start_x = x + xx - 1;
2479 start_y = y + yy - 1;
2482 found_element = element;
2485 if (!CAN_CHANGE(element))
2488 for (i = 0; i < element_info[element].num_change_pages; i++)
2490 /* check for player created from custom element as extended target */
2492 element_info[element].change_page[i].target_content.e[xx][yy];
2494 is_player = ELEM_IS_PLAYER(content);
2496 if (is_player && (found_rating < 1 || element < found_element))
2498 start_x = x + xx - 1;
2499 start_y = y + yy - 1;
2502 found_element = element;
2508 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2509 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2512 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2513 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2518 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2519 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2520 local_player->jx - MIDPOSX);
2522 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2523 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2524 local_player->jy - MIDPOSY);
2527 if (!game.restart_level)
2528 CloseDoor(DOOR_CLOSE_1);
2530 /* !!! FIX THIS (START) !!! */
2531 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2533 InitGameEngine_EM();
2540 /* after drawing the level, correct some elements */
2541 if (game.timegate_time_left == 0)
2542 CloseAllOpenTimegates();
2544 if (setup.soft_scrolling)
2545 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2547 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2550 /* !!! FIX THIS (END) !!! */
2552 if (!game.restart_level)
2554 /* copy default game door content to main double buffer */
2555 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2556 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2559 DrawGameDoorValues();
2561 if (!game.restart_level)
2565 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2566 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2567 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2571 /* copy actual game door content to door double buffer for OpenDoor() */
2572 BlitBitmap(drawto, bitmap_db_door,
2573 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2575 OpenDoor(DOOR_OPEN_ALL);
2577 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2579 if (setup.sound_music)
2582 KeyboardAutoRepeatOffUnlessAutoplay();
2586 for (i = 0; i < MAX_PLAYERS; i++)
2587 printf("Player %d %sactive.\n",
2588 i + 1, (stored_player[i].active ? "" : "not "));
2592 game.restart_level = FALSE;
2595 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2597 /* this is used for non-R'n'D game engines to update certain engine values */
2599 /* needed to determine if sounds are played within the visible screen area */
2600 scroll_x = actual_scroll_x;
2601 scroll_y = actual_scroll_y;
2604 void InitMovDir(int x, int y)
2606 int i, element = Feld[x][y];
2607 static int xy[4][2] =
2614 static int direction[3][4] =
2616 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2617 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2618 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2627 Feld[x][y] = EL_BUG;
2628 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2631 case EL_SPACESHIP_RIGHT:
2632 case EL_SPACESHIP_UP:
2633 case EL_SPACESHIP_LEFT:
2634 case EL_SPACESHIP_DOWN:
2635 Feld[x][y] = EL_SPACESHIP;
2636 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2639 case EL_BD_BUTTERFLY_RIGHT:
2640 case EL_BD_BUTTERFLY_UP:
2641 case EL_BD_BUTTERFLY_LEFT:
2642 case EL_BD_BUTTERFLY_DOWN:
2643 Feld[x][y] = EL_BD_BUTTERFLY;
2644 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2647 case EL_BD_FIREFLY_RIGHT:
2648 case EL_BD_FIREFLY_UP:
2649 case EL_BD_FIREFLY_LEFT:
2650 case EL_BD_FIREFLY_DOWN:
2651 Feld[x][y] = EL_BD_FIREFLY;
2652 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2655 case EL_PACMAN_RIGHT:
2657 case EL_PACMAN_LEFT:
2658 case EL_PACMAN_DOWN:
2659 Feld[x][y] = EL_PACMAN;
2660 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2663 case EL_YAMYAM_LEFT:
2664 case EL_YAMYAM_RIGHT:
2666 case EL_YAMYAM_DOWN:
2667 Feld[x][y] = EL_YAMYAM;
2668 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2671 case EL_SP_SNIKSNAK:
2672 MovDir[x][y] = MV_UP;
2675 case EL_SP_ELECTRON:
2676 MovDir[x][y] = MV_LEFT;
2683 Feld[x][y] = EL_MOLE;
2684 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2688 if (IS_CUSTOM_ELEMENT(element))
2690 struct ElementInfo *ei = &element_info[element];
2691 int move_direction_initial = ei->move_direction_initial;
2692 int move_pattern = ei->move_pattern;
2694 if (move_direction_initial == MV_START_PREVIOUS)
2696 if (MovDir[x][y] != MV_NONE)
2699 move_direction_initial = MV_START_AUTOMATIC;
2702 if (move_direction_initial == MV_START_RANDOM)
2703 MovDir[x][y] = 1 << RND(4);
2704 else if (move_direction_initial & MV_ANY_DIRECTION)
2705 MovDir[x][y] = move_direction_initial;
2706 else if (move_pattern == MV_ALL_DIRECTIONS ||
2707 move_pattern == MV_TURNING_LEFT ||
2708 move_pattern == MV_TURNING_RIGHT ||
2709 move_pattern == MV_TURNING_LEFT_RIGHT ||
2710 move_pattern == MV_TURNING_RIGHT_LEFT ||
2711 move_pattern == MV_TURNING_RANDOM)
2712 MovDir[x][y] = 1 << RND(4);
2713 else if (move_pattern == MV_HORIZONTAL)
2714 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2715 else if (move_pattern == MV_VERTICAL)
2716 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2717 else if (move_pattern & MV_ANY_DIRECTION)
2718 MovDir[x][y] = element_info[element].move_pattern;
2719 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2720 move_pattern == MV_ALONG_RIGHT_SIDE)
2722 /* use random direction as default start direction */
2723 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2724 MovDir[x][y] = 1 << RND(4);
2726 for (i = 0; i < NUM_DIRECTIONS; i++)
2728 int x1 = x + xy[i][0];
2729 int y1 = y + xy[i][1];
2731 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2733 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2734 MovDir[x][y] = direction[0][i];
2736 MovDir[x][y] = direction[1][i];
2745 MovDir[x][y] = 1 << RND(4);
2747 if (element != EL_BUG &&
2748 element != EL_SPACESHIP &&
2749 element != EL_BD_BUTTERFLY &&
2750 element != EL_BD_FIREFLY)
2753 for (i = 0; i < NUM_DIRECTIONS; i++)
2755 int x1 = x + xy[i][0];
2756 int y1 = y + xy[i][1];
2758 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2760 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2762 MovDir[x][y] = direction[0][i];
2765 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2766 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2768 MovDir[x][y] = direction[1][i];
2777 GfxDir[x][y] = MovDir[x][y];
2780 void InitAmoebaNr(int x, int y)
2783 int group_nr = AmoebeNachbarNr(x, y);
2787 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2789 if (AmoebaCnt[i] == 0)
2797 AmoebaNr[x][y] = group_nr;
2798 AmoebaCnt[group_nr]++;
2799 AmoebaCnt2[group_nr]++;
2805 boolean raise_level = FALSE;
2807 if (local_player->MovPos)
2810 if (tape.auto_play) /* tape might already be stopped here */
2811 tape.auto_play_level_solved = TRUE;
2813 local_player->LevelSolved = FALSE;
2815 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2819 if (!tape.playing && setup.sound_loops)
2820 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2821 SND_CTRL_PLAY_LOOP);
2823 while (TimeLeft > 0)
2825 if (!tape.playing && !setup.sound_loops)
2826 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2828 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2831 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2836 RaiseScore(level.score[SC_TIME_BONUS]);
2839 DrawGameValue_Time(TimeLeft);
2847 if (!tape.playing && setup.sound_loops)
2848 StopSound(SND_GAME_LEVELTIME_BONUS);
2850 else if (level.time == 0) /* level without time limit */
2852 if (!tape.playing && setup.sound_loops)
2853 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2854 SND_CTRL_PLAY_LOOP);
2856 while (TimePlayed < 999)
2858 if (!tape.playing && !setup.sound_loops)
2859 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2861 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2864 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2869 RaiseScore(level.score[SC_TIME_BONUS]);
2872 DrawGameValue_Time(TimePlayed);
2880 if (!tape.playing && setup.sound_loops)
2881 StopSound(SND_GAME_LEVELTIME_BONUS);
2884 /* close exit door after last player */
2885 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2886 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2887 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2889 int element = Feld[ExitX][ExitY];
2891 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2892 EL_SP_EXIT_CLOSING);
2894 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2897 /* player disappears */
2898 if (ExitX >= 0 && ExitY >= 0)
2899 DrawLevelField(ExitX, ExitY);
2905 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2907 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2913 CloseDoor(DOOR_CLOSE_1);
2918 SaveTape(tape.level_nr); /* Ask to save tape */
2921 if (level_nr == leveldir_current->handicap_level)
2923 leveldir_current->handicap_level++;
2924 SaveLevelSetup_SeriesInfo();
2927 if (level_editor_test_game)
2928 local_player->score = -1; /* no highscore when playing from editor */
2929 else if (level_nr < leveldir_current->last_level)
2930 raise_level = TRUE; /* advance to next level */
2932 if ((hi_pos = NewHiScore()) >= 0)
2934 game_status = GAME_MODE_SCORES;
2935 DrawHallOfFame(hi_pos);
2944 game_status = GAME_MODE_MAIN;
2961 LoadScore(level_nr);
2963 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2964 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2967 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2969 if (local_player->score > highscore[k].Score)
2971 /* player has made it to the hall of fame */
2973 if (k < MAX_SCORE_ENTRIES - 1)
2975 int m = MAX_SCORE_ENTRIES - 1;
2978 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2979 if (strEqual(setup.player_name, highscore[l].Name))
2981 if (m == k) /* player's new highscore overwrites his old one */
2985 for (l = m; l > k; l--)
2987 strcpy(highscore[l].Name, highscore[l - 1].Name);
2988 highscore[l].Score = highscore[l - 1].Score;
2995 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2996 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2997 highscore[k].Score = local_player->score;
3003 else if (!strncmp(setup.player_name, highscore[k].Name,
3004 MAX_PLAYER_NAME_LEN))
3005 break; /* player already there with a higher score */
3011 SaveScore(level_nr);
3016 inline static int getElementMoveStepsize(int x, int y)
3018 int element = Feld[x][y];
3019 int direction = MovDir[x][y];
3020 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3021 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3022 int horiz_move = (dx != 0);
3023 int sign = (horiz_move ? dx : dy);
3024 int step = sign * element_info[element].move_stepsize;
3026 /* special values for move stepsize for spring and things on conveyor belt */
3030 if (element == EL_SPRING)
3031 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3032 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3033 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3034 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3036 if (CAN_FALL(element) &&
3037 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3038 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3039 else if (element == EL_SPRING)
3040 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3047 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3049 if (player->GfxAction != action || player->GfxDir != dir)
3052 printf("Player frame reset! (%d => %d, %d => %d)\n",
3053 player->GfxAction, action, player->GfxDir, dir);
3056 player->GfxAction = action;
3057 player->GfxDir = dir;
3059 player->StepFrame = 0;
3063 #if USE_GFX_RESET_GFX_ANIMATION
3064 static void ResetGfxFrame(int x, int y, boolean redraw)
3066 int element = Feld[x][y];
3067 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3068 int last_gfx_frame = GfxFrame[x][y];
3070 if (graphic_info[graphic].anim_global_sync)
3071 GfxFrame[x][y] = FrameCounter;
3072 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3073 GfxFrame[x][y] = CustomValue[x][y];
3074 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3075 GfxFrame[x][y] = element_info[element].collect_score;
3076 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3077 GfxFrame[x][y] = ChangeDelay[x][y];
3079 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3080 DrawLevelGraphicAnimation(x, y, graphic);
3084 static void ResetGfxAnimation(int x, int y)
3087 int element, graphic;
3090 GfxAction[x][y] = ACTION_DEFAULT;
3091 GfxDir[x][y] = MovDir[x][y];
3095 element = Feld[x][y];
3096 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3098 if (graphic_info[graphic].anim_global_sync)
3099 GfxFrame[x][y] = FrameCounter;
3100 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3101 GfxFrame[x][y] = CustomValue[x][y];
3102 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3103 GfxFrame[x][y] = element_info[element].collect_score;
3104 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3105 GfxFrame[x][y] = ChangeDelay[x][y];
3108 #if USE_GFX_RESET_GFX_ANIMATION
3109 ResetGfxFrame(x, y, FALSE);
3113 static void ResetRandomAnimationValue(int x, int y)
3115 GfxRandom[x][y] = INIT_GFX_RANDOM();
3118 void InitMovingField(int x, int y, int direction)
3120 int element = Feld[x][y];
3124 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3125 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3129 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3130 ResetGfxAnimation(x, y);
3132 MovDir[x][y] = direction;
3133 GfxDir[x][y] = direction;
3134 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3135 ACTION_FALLING : ACTION_MOVING);
3138 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3140 if (graphic_info[graphic].anim_global_sync)
3141 GfxFrame[x][y] = FrameCounter;
3142 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3143 GfxFrame[x][y] = CustomValue[x][y];
3144 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3145 GfxFrame[x][y] = element_info[element].collect_score;
3146 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3147 GfxFrame[x][y] = ChangeDelay[x][y];
3150 /* this is needed for CEs with property "can move" / "not moving" */
3152 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3154 if (Feld[newx][newy] == EL_EMPTY)
3155 Feld[newx][newy] = EL_BLOCKED;
3157 MovDir[newx][newy] = MovDir[x][y];
3159 #if USE_NEW_CUSTOM_VALUE
3160 CustomValue[newx][newy] = CustomValue[x][y];
3163 GfxFrame[newx][newy] = GfxFrame[x][y];
3164 GfxRandom[newx][newy] = GfxRandom[x][y];
3165 GfxAction[newx][newy] = GfxAction[x][y];
3166 GfxDir[newx][newy] = GfxDir[x][y];
3170 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3172 int direction = MovDir[x][y];
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);
3177 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3178 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3185 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3187 int oldx = x, oldy = y;
3188 int direction = MovDir[x][y];
3190 if (direction == MV_LEFT)
3192 else if (direction == MV_RIGHT)
3194 else if (direction == MV_UP)
3196 else if (direction == MV_DOWN)
3199 *comes_from_x = oldx;
3200 *comes_from_y = oldy;
3203 int MovingOrBlocked2Element(int x, int y)
3205 int element = Feld[x][y];
3207 if (element == EL_BLOCKED)
3211 Blocked2Moving(x, y, &oldx, &oldy);
3212 return Feld[oldx][oldy];
3218 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3220 /* like MovingOrBlocked2Element(), but if element is moving
3221 and (x,y) is the field the moving element is just leaving,
3222 return EL_BLOCKED instead of the element value */
3223 int element = Feld[x][y];
3225 if (IS_MOVING(x, y))
3227 if (element == EL_BLOCKED)
3231 Blocked2Moving(x, y, &oldx, &oldy);
3232 return Feld[oldx][oldy];
3241 static void RemoveField(int x, int y)
3243 Feld[x][y] = EL_EMPTY;
3249 #if USE_NEW_CUSTOM_VALUE
3250 CustomValue[x][y] = 0;
3254 ChangeDelay[x][y] = 0;
3255 ChangePage[x][y] = -1;
3256 Pushed[x][y] = FALSE;
3259 ExplodeField[x][y] = EX_TYPE_NONE;
3262 GfxElement[x][y] = EL_UNDEFINED;
3263 GfxAction[x][y] = ACTION_DEFAULT;
3264 GfxDir[x][y] = MV_NONE;
3267 void RemoveMovingField(int x, int y)
3269 int oldx = x, oldy = y, newx = x, newy = y;
3270 int element = Feld[x][y];
3271 int next_element = EL_UNDEFINED;
3273 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3276 if (IS_MOVING(x, y))
3278 Moving2Blocked(x, y, &newx, &newy);
3280 if (Feld[newx][newy] != EL_BLOCKED)
3282 /* element is moving, but target field is not free (blocked), but
3283 already occupied by something different (example: acid pool);
3284 in this case, only remove the moving field, but not the target */
3286 RemoveField(oldx, oldy);
3288 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3290 DrawLevelField(oldx, oldy);
3295 else if (element == EL_BLOCKED)
3297 Blocked2Moving(x, y, &oldx, &oldy);
3298 if (!IS_MOVING(oldx, oldy))
3302 if (element == EL_BLOCKED &&
3303 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3304 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3305 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3306 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3307 next_element = get_next_element(Feld[oldx][oldy]);
3309 RemoveField(oldx, oldy);
3310 RemoveField(newx, newy);
3312 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3314 if (next_element != EL_UNDEFINED)
3315 Feld[oldx][oldy] = next_element;
3317 DrawLevelField(oldx, oldy);
3318 DrawLevelField(newx, newy);
3321 void DrawDynamite(int x, int y)
3323 int sx = SCREENX(x), sy = SCREENY(y);
3324 int graphic = el2img(Feld[x][y]);
3327 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3330 if (IS_WALKABLE_INSIDE(Back[x][y]))
3334 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3335 else if (Store[x][y])
3336 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3338 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3340 if (Back[x][y] || Store[x][y])
3341 DrawGraphicThruMask(sx, sy, graphic, frame);
3343 DrawGraphic(sx, sy, graphic, frame);
3346 void CheckDynamite(int x, int y)
3348 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3352 if (MovDelay[x][y] != 0)
3355 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3361 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3368 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3370 boolean num_checked_players = 0;
3373 for (i = 0; i < MAX_PLAYERS; i++)
3375 if (stored_player[i].active)
3377 int sx = stored_player[i].jx;
3378 int sy = stored_player[i].jy;
3380 if (num_checked_players == 0)
3387 *sx1 = MIN(*sx1, sx);
3388 *sy1 = MIN(*sy1, sy);
3389 *sx2 = MAX(*sx2, sx);
3390 *sy2 = MAX(*sy2, sy);
3393 num_checked_players++;
3398 static boolean checkIfAllPlayersFitToScreen_RND()
3400 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3402 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3404 return (sx2 - sx1 < SCR_FIELDX &&
3405 sy2 - sy1 < SCR_FIELDY);
3408 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3410 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3412 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3414 *sx = (sx1 + sx2) / 2;
3415 *sy = (sy1 + sy2) / 2;
3419 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3420 int center_x, int center_y)
3422 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3424 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3426 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3427 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3430 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3434 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3436 return (max_dx <= SCR_FIELDX / 2 &&
3437 max_dy <= SCR_FIELDY / 2);
3445 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3446 boolean quick_relocation)
3448 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3449 boolean no_delay = (tape.warp_forward);
3450 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3451 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3453 if (quick_relocation)
3455 int offset = (setup.scroll_delay ? 3 : 0);
3462 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3464 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3465 x > SBX_Right + MIDPOSX ? SBX_Right :
3468 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3469 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3474 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3475 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3476 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3478 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3479 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3480 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3482 /* don't scroll over playfield boundaries */
3483 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3484 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3486 /* don't scroll over playfield boundaries */
3487 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3488 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3491 RedrawPlayfield(TRUE, 0,0,0,0);
3495 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3496 x > SBX_Right + MIDPOSX ? SBX_Right :
3499 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3500 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3503 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3505 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3508 int fx = FX, fy = FY;
3510 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3511 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3513 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3519 fx += dx * TILEX / 2;
3520 fy += dy * TILEY / 2;
3522 ScrollLevel(dx, dy);
3525 /* scroll in two steps of half tile size to make things smoother */
3526 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3528 Delay(wait_delay_value);
3530 /* scroll second step to align at full tile size */
3532 Delay(wait_delay_value);
3537 Delay(wait_delay_value);
3543 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3545 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3546 boolean no_delay = (tape.warp_forward);
3547 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3548 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3549 int jx = player->jx;
3550 int jy = player->jy;
3552 if (quick_relocation)
3554 int offset = (setup.scroll_delay ? 3 : 0);
3556 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3558 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3559 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3560 player->jx - MIDPOSX);
3562 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3563 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3564 player->jy - MIDPOSY);
3568 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3569 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3570 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3572 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3573 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3574 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3576 /* don't scroll over playfield boundaries */
3577 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3578 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3580 /* don't scroll over playfield boundaries */
3581 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3582 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3585 RedrawPlayfield(TRUE, 0,0,0,0);
3589 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3590 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3591 player->jx - MIDPOSX);
3593 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3594 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3595 player->jy - MIDPOSY);
3597 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3599 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3602 int fx = FX, fy = FY;
3604 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3605 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3607 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3613 fx += dx * TILEX / 2;
3614 fy += dy * TILEY / 2;
3616 ScrollLevel(dx, dy);
3619 /* scroll in two steps of half tile size to make things smoother */
3620 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3622 Delay(wait_delay_value);
3624 /* scroll second step to align at full tile size */
3626 Delay(wait_delay_value);
3631 Delay(wait_delay_value);
3637 void RelocatePlayer(int jx, int jy, int el_player_raw)
3639 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3640 int player_nr = GET_PLAYER_NR(el_player);
3641 struct PlayerInfo *player = &stored_player[player_nr];
3642 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3643 boolean no_delay = (tape.warp_forward);
3644 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3645 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3646 int old_jx = player->jx;
3647 int old_jy = player->jy;
3648 int old_element = Feld[old_jx][old_jy];
3649 int element = Feld[jx][jy];
3650 boolean player_relocated = (old_jx != jx || old_jy != jy);
3652 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3653 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3654 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3655 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3656 int leave_side_horiz = move_dir_horiz;
3657 int leave_side_vert = move_dir_vert;
3658 int enter_side = enter_side_horiz | enter_side_vert;
3659 int leave_side = leave_side_horiz | leave_side_vert;
3661 if (player->GameOver) /* do not reanimate dead player */
3664 if (!player_relocated) /* no need to relocate the player */
3667 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3669 RemoveField(jx, jy); /* temporarily remove newly placed player */
3670 DrawLevelField(jx, jy);
3673 if (player->present)
3675 while (player->MovPos)
3677 ScrollPlayer(player, SCROLL_GO_ON);
3678 ScrollScreen(NULL, SCROLL_GO_ON);
3680 AdvanceFrameAndPlayerCounters(player->index_nr);
3685 Delay(wait_delay_value);
3688 DrawPlayer(player); /* needed here only to cleanup last field */
3689 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3691 player->is_moving = FALSE;
3694 if (IS_CUSTOM_ELEMENT(old_element))
3695 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3697 player->index_bit, leave_side);
3699 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3701 player->index_bit, leave_side);
3703 Feld[jx][jy] = el_player;
3704 InitPlayerField(jx, jy, el_player, TRUE);
3706 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3708 Feld[jx][jy] = element;
3709 InitField(jx, jy, FALSE);
3713 /* only visually relocate centered player */
3715 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3716 level.instant_relocation);
3718 if (player->index_nr == game.centered_player_nr)
3719 DrawRelocatePlayer(player, level.instant_relocation);
3722 if (player == local_player) /* only visually relocate local player */
3723 DrawRelocatePlayer(player, level.instant_relocation);
3726 TestIfPlayerTouchesBadThing(jx, jy);
3727 TestIfPlayerTouchesCustomElement(jx, jy);
3729 if (IS_CUSTOM_ELEMENT(element))
3730 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3731 player->index_bit, enter_side);
3733 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3734 player->index_bit, enter_side);
3737 void Explode(int ex, int ey, int phase, int mode)
3743 /* !!! eliminate this variable !!! */
3744 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3746 if (game.explosions_delayed)
3748 ExplodeField[ex][ey] = mode;
3752 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3754 int center_element = Feld[ex][ey];
3755 int artwork_element, explosion_element; /* set these values later */
3758 /* --- This is only really needed (and now handled) in "Impact()". --- */
3759 /* do not explode moving elements that left the explode field in time */
3760 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3761 center_element == EL_EMPTY &&
3762 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3767 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3768 if (mode == EX_TYPE_NORMAL ||
3769 mode == EX_TYPE_CENTER ||
3770 mode == EX_TYPE_CROSS)
3771 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3774 /* remove things displayed in background while burning dynamite */
3775 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3778 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3780 /* put moving element to center field (and let it explode there) */
3781 center_element = MovingOrBlocked2Element(ex, ey);
3782 RemoveMovingField(ex, ey);
3783 Feld[ex][ey] = center_element;
3786 /* now "center_element" is finally determined -- set related values now */
3787 artwork_element = center_element; /* for custom player artwork */
3788 explosion_element = center_element; /* for custom player artwork */
3790 if (IS_PLAYER(ex, ey))
3792 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3794 artwork_element = stored_player[player_nr].artwork_element;
3796 if (level.use_explosion_element[player_nr])
3798 explosion_element = level.explosion_element[player_nr];
3799 artwork_element = explosion_element;
3804 if (mode == EX_TYPE_NORMAL ||
3805 mode == EX_TYPE_CENTER ||
3806 mode == EX_TYPE_CROSS)
3807 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3811 last_phase = element_info[explosion_element].explosion_delay + 1;
3813 last_phase = element_info[center_element].explosion_delay + 1;
3816 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3818 int xx = x - ex + 1;
3819 int yy = y - ey + 1;
3822 if (!IN_LEV_FIELD(x, y) ||
3823 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3824 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3827 element = Feld[x][y];
3829 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3831 element = MovingOrBlocked2Element(x, y);
3833 if (!IS_EXPLOSION_PROOF(element))
3834 RemoveMovingField(x, y);
3837 /* indestructible elements can only explode in center (but not flames) */
3838 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3839 mode == EX_TYPE_BORDER)) ||
3840 element == EL_FLAMES)
3843 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3844 behaviour, for example when touching a yamyam that explodes to rocks
3845 with active deadly shield, a rock is created under the player !!! */
3846 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3848 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3849 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3850 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3852 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3855 if (IS_ACTIVE_BOMB(element))
3857 /* re-activate things under the bomb like gate or penguin */
3858 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3865 /* save walkable background elements while explosion on same tile */
3866 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3867 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3868 Back[x][y] = element;
3870 /* ignite explodable elements reached by other explosion */
3871 if (element == EL_EXPLOSION)
3872 element = Store2[x][y];
3874 if (AmoebaNr[x][y] &&
3875 (element == EL_AMOEBA_FULL ||
3876 element == EL_BD_AMOEBA ||
3877 element == EL_AMOEBA_GROWING))
3879 AmoebaCnt[AmoebaNr[x][y]]--;
3880 AmoebaCnt2[AmoebaNr[x][y]]--;
3885 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3888 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3890 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3892 switch(StorePlayer[ex][ey])
3895 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3898 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3901 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3905 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3910 if (PLAYERINFO(ex, ey)->use_murphy)
3911 Store[x][y] = EL_EMPTY;
3914 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3915 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3916 else if (ELEM_IS_PLAYER(center_element))
3917 Store[x][y] = EL_EMPTY;
3918 else if (center_element == EL_YAMYAM)
3919 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3920 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3921 Store[x][y] = element_info[center_element].content.e[xx][yy];
3923 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3924 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3925 otherwise) -- FIX THIS !!! */
3926 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3927 Store[x][y] = element_info[element].content.e[1][1];
3929 else if (!CAN_EXPLODE(element))
3930 Store[x][y] = element_info[element].content.e[1][1];
3933 Store[x][y] = EL_EMPTY;
3935 else if (center_element == EL_MOLE)
3936 Store[x][y] = EL_EMERALD_RED;
3937 else if (center_element == EL_PENGUIN)
3938 Store[x][y] = EL_EMERALD_PURPLE;
3939 else if (center_element == EL_BUG)
3940 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3941 else if (center_element == EL_BD_BUTTERFLY)
3942 Store[x][y] = EL_BD_DIAMOND;
3943 else if (center_element == EL_SP_ELECTRON)
3944 Store[x][y] = EL_SP_INFOTRON;
3945 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3946 Store[x][y] = level.amoeba_content;
3947 else if (center_element == EL_YAMYAM)
3948 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3949 else if (IS_CUSTOM_ELEMENT(center_element) &&
3950 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3951 Store[x][y] = element_info[center_element].content.e[xx][yy];
3952 else if (element == EL_WALL_EMERALD)
3953 Store[x][y] = EL_EMERALD;
3954 else if (element == EL_WALL_DIAMOND)
3955 Store[x][y] = EL_DIAMOND;
3956 else if (element == EL_WALL_BD_DIAMOND)
3957 Store[x][y] = EL_BD_DIAMOND;
3958 else if (element == EL_WALL_EMERALD_YELLOW)
3959 Store[x][y] = EL_EMERALD_YELLOW;
3960 else if (element == EL_WALL_EMERALD_RED)
3961 Store[x][y] = EL_EMERALD_RED;
3962 else if (element == EL_WALL_EMERALD_PURPLE)
3963 Store[x][y] = EL_EMERALD_PURPLE;
3964 else if (element == EL_WALL_PEARL)
3965 Store[x][y] = EL_PEARL;
3966 else if (element == EL_WALL_CRYSTAL)
3967 Store[x][y] = EL_CRYSTAL;
3968 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3969 Store[x][y] = element_info[element].content.e[1][1];
3971 Store[x][y] = EL_EMPTY;
3974 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3975 center_element == EL_AMOEBA_TO_DIAMOND)
3976 Store2[x][y] = element;
3978 Feld[x][y] = EL_EXPLOSION;
3979 GfxElement[x][y] = artwork_element;
3982 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3983 x, y, artwork_element, EL_NAME(artwork_element));
3986 ExplodePhase[x][y] = 1;
3987 ExplodeDelay[x][y] = last_phase;
3992 if (center_element == EL_YAMYAM)
3993 game.yamyam_content_nr =
3994 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4006 GfxFrame[x][y] = 0; /* restart explosion animation */
4008 last_phase = ExplodeDelay[x][y];
4010 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4014 /* activate this even in non-DEBUG version until cause for crash in
4015 getGraphicAnimationFrame() (see below) is found and eliminated */
4021 /* this can happen if the player leaves an explosion just in time */
4022 if (GfxElement[x][y] == EL_UNDEFINED)
4023 GfxElement[x][y] = EL_EMPTY;
4025 if (GfxElement[x][y] == EL_UNDEFINED)
4028 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4029 printf("Explode(): This should never happen!\n");
4032 GfxElement[x][y] = EL_EMPTY;
4038 border_element = Store2[x][y];
4039 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4040 border_element = StorePlayer[x][y];
4042 if (phase == element_info[border_element].ignition_delay ||
4043 phase == last_phase)
4045 boolean border_explosion = FALSE;
4047 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4048 !PLAYER_EXPLOSION_PROTECTED(x, y))
4050 KillPlayerUnlessExplosionProtected(x, y);
4051 border_explosion = TRUE;
4053 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4055 Feld[x][y] = Store2[x][y];
4058 border_explosion = TRUE;
4060 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4062 AmoebeUmwandeln(x, y);
4064 border_explosion = TRUE;
4067 /* if an element just explodes due to another explosion (chain-reaction),
4068 do not immediately end the new explosion when it was the last frame of
4069 the explosion (as it would be done in the following "if"-statement!) */
4070 if (border_explosion && phase == last_phase)
4074 if (phase == last_phase)
4078 element = Feld[x][y] = Store[x][y];
4079 Store[x][y] = Store2[x][y] = 0;
4080 GfxElement[x][y] = EL_UNDEFINED;
4082 /* player can escape from explosions and might therefore be still alive */
4083 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4084 element <= EL_PLAYER_IS_EXPLODING_4)
4086 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4087 int explosion_element = EL_PLAYER_1 + player_nr;
4088 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4089 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4091 if (level.use_explosion_element[player_nr])
4092 explosion_element = level.explosion_element[player_nr];
4094 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4095 element_info[explosion_element].content.e[xx][yy]);
4098 /* restore probably existing indestructible background element */
4099 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4100 element = Feld[x][y] = Back[x][y];
4103 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4104 GfxDir[x][y] = MV_NONE;
4105 ChangeDelay[x][y] = 0;
4106 ChangePage[x][y] = -1;
4108 #if USE_NEW_CUSTOM_VALUE
4109 CustomValue[x][y] = 0;
4112 InitField_WithBug2(x, y, FALSE);
4114 DrawLevelField(x, y);
4116 TestIfElementTouchesCustomElement(x, y);
4118 if (GFX_CRUMBLED(element))
4119 DrawLevelFieldCrumbledSandNeighbours(x, y);
4121 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4122 StorePlayer[x][y] = 0;
4124 if (ELEM_IS_PLAYER(element))
4125 RelocatePlayer(x, y, element);
4127 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4129 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4130 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4133 DrawLevelFieldCrumbledSand(x, y);
4135 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4137 DrawLevelElement(x, y, Back[x][y]);
4138 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4140 else if (IS_WALKABLE_UNDER(Back[x][y]))
4142 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4143 DrawLevelElementThruMask(x, y, Back[x][y]);
4145 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4146 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4150 void DynaExplode(int ex, int ey)
4153 int dynabomb_element = Feld[ex][ey];
4154 int dynabomb_size = 1;
4155 boolean dynabomb_xl = FALSE;
4156 struct PlayerInfo *player;
4157 static int xy[4][2] =
4165 if (IS_ACTIVE_BOMB(dynabomb_element))
4167 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4168 dynabomb_size = player->dynabomb_size;
4169 dynabomb_xl = player->dynabomb_xl;
4170 player->dynabombs_left++;
4173 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4175 for (i = 0; i < NUM_DIRECTIONS; i++)
4177 for (j = 1; j <= dynabomb_size; j++)
4179 int x = ex + j * xy[i][0];
4180 int y = ey + j * xy[i][1];
4183 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4186 element = Feld[x][y];
4188 /* do not restart explosions of fields with active bombs */
4189 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4192 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4194 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4195 !IS_DIGGABLE(element) && !dynabomb_xl)
4201 void Bang(int x, int y)
4203 int element = MovingOrBlocked2Element(x, y);
4204 int explosion_type = EX_TYPE_NORMAL;
4206 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4208 struct PlayerInfo *player = PLAYERINFO(x, y);
4210 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4211 player->element_nr);
4213 if (level.use_explosion_element[player->index_nr])
4215 int explosion_element = level.explosion_element[player->index_nr];
4217 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4218 explosion_type = EX_TYPE_CROSS;
4219 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4220 explosion_type = EX_TYPE_CENTER;
4228 case EL_BD_BUTTERFLY:
4231 case EL_DARK_YAMYAM:
4235 RaiseScoreElement(element);
4238 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4239 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4240 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4241 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4242 case EL_DYNABOMB_INCREASE_NUMBER:
4243 case EL_DYNABOMB_INCREASE_SIZE:
4244 case EL_DYNABOMB_INCREASE_POWER:
4245 explosion_type = EX_TYPE_DYNA;
4250 case EL_LAMP_ACTIVE:
4251 case EL_AMOEBA_TO_DIAMOND:
4252 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4253 explosion_type = EX_TYPE_CENTER;
4257 if (element_info[element].explosion_type == EXPLODES_CROSS)
4258 explosion_type = EX_TYPE_CROSS;
4259 else if (element_info[element].explosion_type == EXPLODES_1X1)
4260 explosion_type = EX_TYPE_CENTER;
4264 if (explosion_type == EX_TYPE_DYNA)
4267 Explode(x, y, EX_PHASE_START, explosion_type);
4269 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4272 void SplashAcid(int x, int y)
4274 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4275 (!IN_LEV_FIELD(x - 1, y - 2) ||
4276 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4277 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4279 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4280 (!IN_LEV_FIELD(x + 1, y - 2) ||
4281 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4282 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4284 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4287 static void InitBeltMovement()
4289 static int belt_base_element[4] =
4291 EL_CONVEYOR_BELT_1_LEFT,
4292 EL_CONVEYOR_BELT_2_LEFT,
4293 EL_CONVEYOR_BELT_3_LEFT,
4294 EL_CONVEYOR_BELT_4_LEFT
4296 static int belt_base_active_element[4] =
4298 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4299 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4300 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4301 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4306 /* set frame order for belt animation graphic according to belt direction */
4307 for (i = 0; i < NUM_BELTS; i++)
4311 for (j = 0; j < NUM_BELT_PARTS; j++)
4313 int element = belt_base_active_element[belt_nr] + j;
4314 int graphic = el2img(element);
4316 if (game.belt_dir[i] == MV_LEFT)
4317 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4319 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4324 SCAN_PLAYFIELD(x, y)
4326 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4329 int element = Feld[x][y];
4331 for (i = 0; i < NUM_BELTS; i++)
4333 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4335 int e_belt_nr = getBeltNrFromBeltElement(element);
4338 if (e_belt_nr == belt_nr)
4340 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4342 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4349 static void ToggleBeltSwitch(int x, int y)
4351 static int belt_base_element[4] =
4353 EL_CONVEYOR_BELT_1_LEFT,
4354 EL_CONVEYOR_BELT_2_LEFT,
4355 EL_CONVEYOR_BELT_3_LEFT,
4356 EL_CONVEYOR_BELT_4_LEFT
4358 static int belt_base_active_element[4] =
4360 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4361 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4362 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4363 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4365 static int belt_base_switch_element[4] =
4367 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4368 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4369 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4370 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4372 static int belt_move_dir[4] =
4380 int element = Feld[x][y];
4381 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4382 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4383 int belt_dir = belt_move_dir[belt_dir_nr];
4386 if (!IS_BELT_SWITCH(element))
4389 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4390 game.belt_dir[belt_nr] = belt_dir;
4392 if (belt_dir_nr == 3)
4395 /* set frame order for belt animation graphic according to belt direction */
4396 for (i = 0; i < NUM_BELT_PARTS; i++)
4398 int element = belt_base_active_element[belt_nr] + i;
4399 int graphic = el2img(element);
4401 if (belt_dir == MV_LEFT)
4402 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4404 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4408 SCAN_PLAYFIELD(xx, yy)
4410 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4413 int element = Feld[xx][yy];
4415 if (IS_BELT_SWITCH(element))
4417 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4419 if (e_belt_nr == belt_nr)
4421 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4422 DrawLevelField(xx, yy);
4425 else if (IS_BELT(element) && belt_dir != MV_NONE)
4427 int e_belt_nr = getBeltNrFromBeltElement(element);
4429 if (e_belt_nr == belt_nr)
4431 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4433 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4434 DrawLevelField(xx, yy);
4437 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4439 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4441 if (e_belt_nr == belt_nr)
4443 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4445 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4446 DrawLevelField(xx, yy);
4452 static void ToggleSwitchgateSwitch(int x, int y)
4456 game.switchgate_pos = !game.switchgate_pos;
4459 SCAN_PLAYFIELD(xx, yy)
4461 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4464 int element = Feld[xx][yy];
4466 #if !USE_BOTH_SWITCHGATE_SWITCHES
4467 if (element == EL_SWITCHGATE_SWITCH_UP ||
4468 element == EL_SWITCHGATE_SWITCH_DOWN)
4470 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4471 DrawLevelField(xx, yy);
4474 if (element == EL_SWITCHGATE_SWITCH_UP)
4476 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4477 DrawLevelField(xx, yy);
4479 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4481 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4482 DrawLevelField(xx, yy);
4485 else if (element == EL_SWITCHGATE_OPEN ||
4486 element == EL_SWITCHGATE_OPENING)
4488 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4490 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4492 else if (element == EL_SWITCHGATE_CLOSED ||
4493 element == EL_SWITCHGATE_CLOSING)
4495 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4497 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4502 static int getInvisibleActiveFromInvisibleElement(int element)
4504 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4505 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4506 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4510 static int getInvisibleFromInvisibleActiveElement(int element)
4512 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4513 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4514 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4518 static void RedrawAllLightSwitchesAndInvisibleElements()
4523 SCAN_PLAYFIELD(x, y)
4525 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4528 int element = Feld[x][y];
4530 if (element == EL_LIGHT_SWITCH &&
4531 game.light_time_left > 0)
4533 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4534 DrawLevelField(x, y);
4536 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4537 game.light_time_left == 0)
4539 Feld[x][y] = EL_LIGHT_SWITCH;
4540 DrawLevelField(x, y);
4542 else if (element == EL_EMC_DRIPPER &&
4543 game.light_time_left > 0)
4545 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4546 DrawLevelField(x, y);
4548 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4549 game.light_time_left == 0)
4551 Feld[x][y] = EL_EMC_DRIPPER;
4552 DrawLevelField(x, y);
4554 else if (element == EL_INVISIBLE_STEELWALL ||
4555 element == EL_INVISIBLE_WALL ||
4556 element == EL_INVISIBLE_SAND)
4558 if (game.light_time_left > 0)
4559 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4561 DrawLevelField(x, y);
4563 /* uncrumble neighbour fields, if needed */
4564 if (element == EL_INVISIBLE_SAND)
4565 DrawLevelFieldCrumbledSandNeighbours(x, y);
4567 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4568 element == EL_INVISIBLE_WALL_ACTIVE ||
4569 element == EL_INVISIBLE_SAND_ACTIVE)
4571 if (game.light_time_left == 0)
4572 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4574 DrawLevelField(x, y);
4576 /* re-crumble neighbour fields, if needed */
4577 if (element == EL_INVISIBLE_SAND)
4578 DrawLevelFieldCrumbledSandNeighbours(x, y);
4583 static void RedrawAllInvisibleElementsForLenses()
4588 SCAN_PLAYFIELD(x, y)
4590 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4593 int element = Feld[x][y];
4595 if (element == EL_EMC_DRIPPER &&
4596 game.lenses_time_left > 0)
4598 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4599 DrawLevelField(x, y);
4601 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4602 game.lenses_time_left == 0)
4604 Feld[x][y] = EL_EMC_DRIPPER;
4605 DrawLevelField(x, y);
4607 else if (element == EL_INVISIBLE_STEELWALL ||
4608 element == EL_INVISIBLE_WALL ||
4609 element == EL_INVISIBLE_SAND)
4611 if (game.lenses_time_left > 0)
4612 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4614 DrawLevelField(x, y);
4616 /* uncrumble neighbour fields, if needed */
4617 if (element == EL_INVISIBLE_SAND)
4618 DrawLevelFieldCrumbledSandNeighbours(x, y);
4620 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4621 element == EL_INVISIBLE_WALL_ACTIVE ||
4622 element == EL_INVISIBLE_SAND_ACTIVE)
4624 if (game.lenses_time_left == 0)
4625 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4627 DrawLevelField(x, y);
4629 /* re-crumble neighbour fields, if needed */
4630 if (element == EL_INVISIBLE_SAND)
4631 DrawLevelFieldCrumbledSandNeighbours(x, y);
4636 static void RedrawAllInvisibleElementsForMagnifier()
4641 SCAN_PLAYFIELD(x, y)
4643 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4646 int element = Feld[x][y];
4648 if (element == EL_EMC_FAKE_GRASS &&
4649 game.magnify_time_left > 0)
4651 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4652 DrawLevelField(x, y);
4654 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4655 game.magnify_time_left == 0)
4657 Feld[x][y] = EL_EMC_FAKE_GRASS;
4658 DrawLevelField(x, y);
4660 else if (IS_GATE_GRAY(element) &&
4661 game.magnify_time_left > 0)
4663 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4664 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4665 IS_EM_GATE_GRAY(element) ?
4666 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4667 IS_EMC_GATE_GRAY(element) ?
4668 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4670 DrawLevelField(x, y);
4672 else if (IS_GATE_GRAY_ACTIVE(element) &&
4673 game.magnify_time_left == 0)
4675 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4676 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4677 IS_EM_GATE_GRAY_ACTIVE(element) ?
4678 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4679 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4680 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4682 DrawLevelField(x, y);
4687 static void ToggleLightSwitch(int x, int y)
4689 int element = Feld[x][y];
4691 game.light_time_left =
4692 (element == EL_LIGHT_SWITCH ?
4693 level.time_light * FRAMES_PER_SECOND : 0);
4695 RedrawAllLightSwitchesAndInvisibleElements();
4698 static void ActivateTimegateSwitch(int x, int y)
4702 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4705 SCAN_PLAYFIELD(xx, yy)
4707 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4710 int element = Feld[xx][yy];
4712 if (element == EL_TIMEGATE_CLOSED ||
4713 element == EL_TIMEGATE_CLOSING)
4715 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4716 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4720 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4722 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4723 DrawLevelField(xx, yy);
4729 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4732 void Impact(int x, int y)
4734 boolean last_line = (y == lev_fieldy - 1);
4735 boolean object_hit = FALSE;
4736 boolean impact = (last_line || object_hit);
4737 int element = Feld[x][y];
4738 int smashed = EL_STEELWALL;
4740 if (!last_line) /* check if element below was hit */
4742 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4745 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4746 MovDir[x][y + 1] != MV_DOWN ||
4747 MovPos[x][y + 1] <= TILEY / 2));
4749 /* do not smash moving elements that left the smashed field in time */
4750 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4751 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4754 #if USE_QUICKSAND_IMPACT_BUGFIX
4755 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4757 RemoveMovingField(x, y + 1);
4758 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4759 Feld[x][y + 2] = EL_ROCK;
4760 DrawLevelField(x, y + 2);
4767 smashed = MovingOrBlocked2Element(x, y + 1);
4769 impact = (last_line || object_hit);
4772 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4774 SplashAcid(x, y + 1);
4778 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4779 /* only reset graphic animation if graphic really changes after impact */
4781 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4783 ResetGfxAnimation(x, y);
4784 DrawLevelField(x, y);
4787 if (impact && CAN_EXPLODE_IMPACT(element))
4792 else if (impact && element == EL_PEARL)
4794 ResetGfxAnimation(x, y);
4796 Feld[x][y] = EL_PEARL_BREAKING;
4797 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4800 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4802 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4807 if (impact && element == EL_AMOEBA_DROP)
4809 if (object_hit && IS_PLAYER(x, y + 1))
4810 KillPlayerUnlessEnemyProtected(x, y + 1);
4811 else if (object_hit && smashed == EL_PENGUIN)
4815 Feld[x][y] = EL_AMOEBA_GROWING;
4816 Store[x][y] = EL_AMOEBA_WET;
4818 ResetRandomAnimationValue(x, y);
4823 if (object_hit) /* check which object was hit */
4825 if (CAN_PASS_MAGIC_WALL(element) &&
4826 (smashed == EL_MAGIC_WALL ||
4827 smashed == EL_BD_MAGIC_WALL))
4830 int activated_magic_wall =
4831 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4832 EL_BD_MAGIC_WALL_ACTIVE);
4834 /* activate magic wall / mill */
4836 SCAN_PLAYFIELD(xx, yy)
4838 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4840 if (Feld[xx][yy] == smashed)
4841 Feld[xx][yy] = activated_magic_wall;
4843 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4844 game.magic_wall_active = TRUE;
4846 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4847 SND_MAGIC_WALL_ACTIVATING :
4848 SND_BD_MAGIC_WALL_ACTIVATING));
4851 if (IS_PLAYER(x, y + 1))
4853 if (CAN_SMASH_PLAYER(element))
4855 KillPlayerUnlessEnemyProtected(x, y + 1);
4859 else if (smashed == EL_PENGUIN)
4861 if (CAN_SMASH_PLAYER(element))
4867 else if (element == EL_BD_DIAMOND)
4869 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4875 else if (((element == EL_SP_INFOTRON ||
4876 element == EL_SP_ZONK) &&
4877 (smashed == EL_SP_SNIKSNAK ||
4878 smashed == EL_SP_ELECTRON ||
4879 smashed == EL_SP_DISK_ORANGE)) ||
4880 (element == EL_SP_INFOTRON &&
4881 smashed == EL_SP_DISK_YELLOW))
4886 else if (CAN_SMASH_EVERYTHING(element))
4888 if (IS_CLASSIC_ENEMY(smashed) ||
4889 CAN_EXPLODE_SMASHED(smashed))
4894 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4896 if (smashed == EL_LAMP ||
4897 smashed == EL_LAMP_ACTIVE)
4902 else if (smashed == EL_NUT)
4904 Feld[x][y + 1] = EL_NUT_BREAKING;
4905 PlayLevelSound(x, y, SND_NUT_BREAKING);
4906 RaiseScoreElement(EL_NUT);
4909 else if (smashed == EL_PEARL)
4911 ResetGfxAnimation(x, y);
4913 Feld[x][y + 1] = EL_PEARL_BREAKING;
4914 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4917 else if (smashed == EL_DIAMOND)
4919 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4920 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4923 else if (IS_BELT_SWITCH(smashed))
4925 ToggleBeltSwitch(x, y + 1);
4927 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4928 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4930 ToggleSwitchgateSwitch(x, y + 1);
4932 else if (smashed == EL_LIGHT_SWITCH ||
4933 smashed == EL_LIGHT_SWITCH_ACTIVE)
4935 ToggleLightSwitch(x, y + 1);
4940 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4943 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4945 CheckElementChangeBySide(x, y + 1, smashed, element,
4946 CE_SWITCHED, CH_SIDE_TOP);
4947 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4953 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4958 /* play sound of magic wall / mill */
4960 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4961 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4963 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4964 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4965 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4966 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4971 /* play sound of object that hits the ground */
4972 if (last_line || object_hit)
4973 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4976 inline static void TurnRoundExt(int x, int y)
4988 { 0, 0 }, { 0, 0 }, { 0, 0 },
4993 int left, right, back;
4997 { MV_DOWN, MV_UP, MV_RIGHT },
4998 { MV_UP, MV_DOWN, MV_LEFT },
5000 { MV_LEFT, MV_RIGHT, MV_DOWN },
5004 { MV_RIGHT, MV_LEFT, MV_UP }
5007 int element = Feld[x][y];
5008 int move_pattern = element_info[element].move_pattern;
5010 int old_move_dir = MovDir[x][y];
5011 int left_dir = turn[old_move_dir].left;
5012 int right_dir = turn[old_move_dir].right;
5013 int back_dir = turn[old_move_dir].back;
5015 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5016 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5017 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5018 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5020 int left_x = x + left_dx, left_y = y + left_dy;
5021 int right_x = x + right_dx, right_y = y + right_dy;
5022 int move_x = x + move_dx, move_y = y + move_dy;
5026 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5028 TestIfBadThingTouchesOtherBadThing(x, y);
5030 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5031 MovDir[x][y] = right_dir;
5032 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5033 MovDir[x][y] = left_dir;
5035 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5037 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5040 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5042 TestIfBadThingTouchesOtherBadThing(x, y);
5044 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5045 MovDir[x][y] = left_dir;
5046 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5047 MovDir[x][y] = right_dir;
5049 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5051 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5054 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5056 TestIfBadThingTouchesOtherBadThing(x, y);
5058 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5059 MovDir[x][y] = left_dir;
5060 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5061 MovDir[x][y] = right_dir;
5063 if (MovDir[x][y] != old_move_dir)
5066 else if (element == EL_YAMYAM)
5068 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5069 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5071 if (can_turn_left && can_turn_right)
5072 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5073 else if (can_turn_left)
5074 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5075 else if (can_turn_right)
5076 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5078 MovDir[x][y] = back_dir;
5080 MovDelay[x][y] = 16 + 16 * RND(3);
5082 else if (element == EL_DARK_YAMYAM)
5084 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5086 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5089 if (can_turn_left && can_turn_right)
5090 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5091 else if (can_turn_left)
5092 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5093 else if (can_turn_right)
5094 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5096 MovDir[x][y] = back_dir;
5098 MovDelay[x][y] = 16 + 16 * RND(3);
5100 else if (element == EL_PACMAN)
5102 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5103 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5105 if (can_turn_left && can_turn_right)
5106 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5107 else if (can_turn_left)
5108 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5109 else if (can_turn_right)
5110 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5112 MovDir[x][y] = back_dir;
5114 MovDelay[x][y] = 6 + RND(40);
5116 else if (element == EL_PIG)
5118 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5119 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5120 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5121 boolean should_turn_left, should_turn_right, should_move_on;
5123 int rnd = RND(rnd_value);
5125 should_turn_left = (can_turn_left &&
5127 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5128 y + back_dy + left_dy)));
5129 should_turn_right = (can_turn_right &&
5131 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5132 y + back_dy + right_dy)));
5133 should_move_on = (can_move_on &&
5136 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5137 y + move_dy + left_dy) ||
5138 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5139 y + move_dy + right_dy)));
5141 if (should_turn_left || should_turn_right || should_move_on)
5143 if (should_turn_left && should_turn_right && should_move_on)
5144 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5145 rnd < 2 * rnd_value / 3 ? right_dir :
5147 else if (should_turn_left && should_turn_right)
5148 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5149 else if (should_turn_left && should_move_on)
5150 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5151 else if (should_turn_right && should_move_on)
5152 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5153 else if (should_turn_left)
5154 MovDir[x][y] = left_dir;
5155 else if (should_turn_right)
5156 MovDir[x][y] = right_dir;
5157 else if (should_move_on)
5158 MovDir[x][y] = old_move_dir;
5160 else if (can_move_on && rnd > rnd_value / 8)
5161 MovDir[x][y] = old_move_dir;
5162 else if (can_turn_left && can_turn_right)
5163 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5164 else if (can_turn_left && rnd > rnd_value / 8)
5165 MovDir[x][y] = left_dir;
5166 else if (can_turn_right && rnd > rnd_value/8)
5167 MovDir[x][y] = right_dir;
5169 MovDir[x][y] = back_dir;
5171 xx = x + move_xy[MovDir[x][y]].dx;
5172 yy = y + move_xy[MovDir[x][y]].dy;
5174 if (!IN_LEV_FIELD(xx, yy) ||
5175 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5176 MovDir[x][y] = old_move_dir;
5180 else if (element == EL_DRAGON)
5182 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5183 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5184 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5186 int rnd = RND(rnd_value);
5188 if (can_move_on && rnd > rnd_value / 8)
5189 MovDir[x][y] = old_move_dir;
5190 else if (can_turn_left && can_turn_right)
5191 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5192 else if (can_turn_left && rnd > rnd_value / 8)
5193 MovDir[x][y] = left_dir;
5194 else if (can_turn_right && rnd > rnd_value / 8)
5195 MovDir[x][y] = right_dir;
5197 MovDir[x][y] = back_dir;
5199 xx = x + move_xy[MovDir[x][y]].dx;
5200 yy = y + move_xy[MovDir[x][y]].dy;
5202 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5203 MovDir[x][y] = old_move_dir;
5207 else if (element == EL_MOLE)
5209 boolean can_move_on =
5210 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5211 IS_AMOEBOID(Feld[move_x][move_y]) ||
5212 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5215 boolean can_turn_left =
5216 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5217 IS_AMOEBOID(Feld[left_x][left_y])));
5219 boolean can_turn_right =
5220 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5221 IS_AMOEBOID(Feld[right_x][right_y])));
5223 if (can_turn_left && can_turn_right)
5224 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5225 else if (can_turn_left)
5226 MovDir[x][y] = left_dir;
5228 MovDir[x][y] = right_dir;
5231 if (MovDir[x][y] != old_move_dir)
5234 else if (element == EL_BALLOON)
5236 MovDir[x][y] = game.wind_direction;
5239 else if (element == EL_SPRING)
5241 #if USE_NEW_SPRING_BUMPER
5242 if (MovDir[x][y] & MV_HORIZONTAL)
5244 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5245 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5247 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5248 ResetGfxAnimation(move_x, move_y);
5249 DrawLevelField(move_x, move_y);
5251 MovDir[x][y] = back_dir;
5253 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5254 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5255 MovDir[x][y] = MV_NONE;
5258 if (MovDir[x][y] & MV_HORIZONTAL &&
5259 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5260 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5261 MovDir[x][y] = MV_NONE;
5266 else if (element == EL_ROBOT ||
5267 element == EL_SATELLITE ||
5268 element == EL_PENGUIN ||
5269 element == EL_EMC_ANDROID)
5271 int attr_x = -1, attr_y = -1;
5282 for (i = 0; i < MAX_PLAYERS; i++)
5284 struct PlayerInfo *player = &stored_player[i];
5285 int jx = player->jx, jy = player->jy;
5287 if (!player->active)
5291 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5299 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5300 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5301 game.engine_version < VERSION_IDENT(3,1,0,0)))
5307 if (element == EL_PENGUIN)
5310 static int xy[4][2] =
5318 for (i = 0; i < NUM_DIRECTIONS; i++)
5320 int ex = x + xy[i][0];
5321 int ey = y + xy[i][1];
5323 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5332 MovDir[x][y] = MV_NONE;
5334 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5335 else if (attr_x > x)
5336 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5338 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5339 else if (attr_y > y)
5340 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5342 if (element == EL_ROBOT)
5346 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5347 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5348 Moving2Blocked(x, y, &newx, &newy);
5350 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5351 MovDelay[x][y] = 8 + 8 * !RND(3);
5353 MovDelay[x][y] = 16;
5355 else if (element == EL_PENGUIN)
5361 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5363 boolean first_horiz = RND(2);
5364 int new_move_dir = MovDir[x][y];
5367 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5368 Moving2Blocked(x, y, &newx, &newy);
5370 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5374 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5375 Moving2Blocked(x, y, &newx, &newy);
5377 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5380 MovDir[x][y] = old_move_dir;
5384 else if (element == EL_SATELLITE)
5390 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5392 boolean first_horiz = RND(2);
5393 int new_move_dir = MovDir[x][y];
5396 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5397 Moving2Blocked(x, y, &newx, &newy);
5399 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5403 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5404 Moving2Blocked(x, y, &newx, &newy);
5406 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5409 MovDir[x][y] = old_move_dir;
5413 else if (element == EL_EMC_ANDROID)
5415 static int check_pos[16] =
5417 -1, /* 0 => (invalid) */
5418 7, /* 1 => MV_LEFT */
5419 3, /* 2 => MV_RIGHT */
5420 -1, /* 3 => (invalid) */
5422 0, /* 5 => MV_LEFT | MV_UP */
5423 2, /* 6 => MV_RIGHT | MV_UP */
5424 -1, /* 7 => (invalid) */
5425 5, /* 8 => MV_DOWN */
5426 6, /* 9 => MV_LEFT | MV_DOWN */
5427 4, /* 10 => MV_RIGHT | MV_DOWN */
5428 -1, /* 11 => (invalid) */
5429 -1, /* 12 => (invalid) */
5430 -1, /* 13 => (invalid) */
5431 -1, /* 14 => (invalid) */
5432 -1, /* 15 => (invalid) */
5440 { -1, -1, MV_LEFT | MV_UP },
5442 { +1, -1, MV_RIGHT | MV_UP },
5443 { +1, 0, MV_RIGHT },
5444 { +1, +1, MV_RIGHT | MV_DOWN },
5446 { -1, +1, MV_LEFT | MV_DOWN },
5449 int start_pos, check_order;
5450 boolean can_clone = FALSE;
5453 /* check if there is any free field around current position */
5454 for (i = 0; i < 8; i++)
5456 int newx = x + check_xy[i].dx;
5457 int newy = y + check_xy[i].dy;
5459 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5467 if (can_clone) /* randomly find an element to clone */
5471 start_pos = check_pos[RND(8)];
5472 check_order = (RND(2) ? -1 : +1);
5474 for (i = 0; i < 8; i++)
5476 int pos_raw = start_pos + i * check_order;
5477 int pos = (pos_raw + 8) % 8;
5478 int newx = x + check_xy[pos].dx;
5479 int newy = y + check_xy[pos].dy;
5481 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5483 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5484 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5486 Store[x][y] = Feld[newx][newy];
5495 if (can_clone) /* randomly find a direction to move */
5499 start_pos = check_pos[RND(8)];
5500 check_order = (RND(2) ? -1 : +1);
5502 for (i = 0; i < 8; i++)
5504 int pos_raw = start_pos + i * check_order;
5505 int pos = (pos_raw + 8) % 8;
5506 int newx = x + check_xy[pos].dx;
5507 int newy = y + check_xy[pos].dy;
5508 int new_move_dir = check_xy[pos].dir;
5510 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5512 MovDir[x][y] = new_move_dir;
5513 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5522 if (can_clone) /* cloning and moving successful */
5525 /* cannot clone -- try to move towards player */
5527 start_pos = check_pos[MovDir[x][y] & 0x0f];
5528 check_order = (RND(2) ? -1 : +1);
5530 for (i = 0; i < 3; i++)
5532 /* first check start_pos, then previous/next or (next/previous) pos */
5533 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5534 int pos = (pos_raw + 8) % 8;
5535 int newx = x + check_xy[pos].dx;
5536 int newy = y + check_xy[pos].dy;
5537 int new_move_dir = check_xy[pos].dir;
5539 if (IS_PLAYER(newx, newy))
5542 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5544 MovDir[x][y] = new_move_dir;
5545 MovDelay[x][y] = level.android_move_time * 8 + 1;
5552 else if (move_pattern == MV_TURNING_LEFT ||
5553 move_pattern == MV_TURNING_RIGHT ||
5554 move_pattern == MV_TURNING_LEFT_RIGHT ||
5555 move_pattern == MV_TURNING_RIGHT_LEFT ||
5556 move_pattern == MV_TURNING_RANDOM ||
5557 move_pattern == MV_ALL_DIRECTIONS)
5559 boolean can_turn_left =
5560 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5561 boolean can_turn_right =
5562 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5564 if (element_info[element].move_stepsize == 0) /* "not moving" */
5567 if (move_pattern == MV_TURNING_LEFT)
5568 MovDir[x][y] = left_dir;
5569 else if (move_pattern == MV_TURNING_RIGHT)
5570 MovDir[x][y] = right_dir;
5571 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5572 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5573 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5574 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5575 else if (move_pattern == MV_TURNING_RANDOM)
5576 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5577 can_turn_right && !can_turn_left ? right_dir :
5578 RND(2) ? left_dir : right_dir);
5579 else if (can_turn_left && can_turn_right)
5580 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5581 else if (can_turn_left)
5582 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5583 else if (can_turn_right)
5584 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5586 MovDir[x][y] = back_dir;
5588 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5590 else if (move_pattern == MV_HORIZONTAL ||
5591 move_pattern == MV_VERTICAL)
5593 if (move_pattern & old_move_dir)
5594 MovDir[x][y] = back_dir;
5595 else if (move_pattern == MV_HORIZONTAL)
5596 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5597 else if (move_pattern == MV_VERTICAL)
5598 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5600 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5602 else if (move_pattern & MV_ANY_DIRECTION)
5604 MovDir[x][y] = move_pattern;
5605 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5607 else if (move_pattern & MV_WIND_DIRECTION)
5609 MovDir[x][y] = game.wind_direction;
5610 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5612 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5614 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5615 MovDir[x][y] = left_dir;
5616 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5617 MovDir[x][y] = right_dir;
5619 if (MovDir[x][y] != old_move_dir)
5620 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5622 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5624 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5625 MovDir[x][y] = right_dir;
5626 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5627 MovDir[x][y] = left_dir;
5629 if (MovDir[x][y] != old_move_dir)
5630 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5632 else if (move_pattern == MV_TOWARDS_PLAYER ||
5633 move_pattern == MV_AWAY_FROM_PLAYER)
5635 int attr_x = -1, attr_y = -1;
5637 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5648 for (i = 0; i < MAX_PLAYERS; i++)
5650 struct PlayerInfo *player = &stored_player[i];
5651 int jx = player->jx, jy = player->jy;
5653 if (!player->active)
5657 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5665 MovDir[x][y] = MV_NONE;
5667 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5668 else if (attr_x > x)
5669 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5671 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5672 else if (attr_y > y)
5673 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5675 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5677 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5679 boolean first_horiz = RND(2);
5680 int new_move_dir = MovDir[x][y];
5682 if (element_info[element].move_stepsize == 0) /* "not moving" */
5684 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5685 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5691 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5692 Moving2Blocked(x, y, &newx, &newy);
5694 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5698 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5699 Moving2Blocked(x, y, &newx, &newy);
5701 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5704 MovDir[x][y] = old_move_dir;
5707 else if (move_pattern == MV_WHEN_PUSHED ||
5708 move_pattern == MV_WHEN_DROPPED)
5710 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5711 MovDir[x][y] = MV_NONE;
5715 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5717 static int test_xy[7][2] =
5727 static int test_dir[7] =
5737 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5738 int move_preference = -1000000; /* start with very low preference */
5739 int new_move_dir = MV_NONE;
5740 int start_test = RND(4);
5743 for (i = 0; i < NUM_DIRECTIONS; i++)
5745 int move_dir = test_dir[start_test + i];
5746 int move_dir_preference;
5748 xx = x + test_xy[start_test + i][0];
5749 yy = y + test_xy[start_test + i][1];
5751 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5752 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5754 new_move_dir = move_dir;
5759 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5762 move_dir_preference = -1 * RunnerVisit[xx][yy];
5763 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5764 move_dir_preference = PlayerVisit[xx][yy];
5766 if (move_dir_preference > move_preference)
5768 /* prefer field that has not been visited for the longest time */
5769 move_preference = move_dir_preference;
5770 new_move_dir = move_dir;
5772 else if (move_dir_preference == move_preference &&
5773 move_dir == old_move_dir)
5775 /* prefer last direction when all directions are preferred equally */
5776 move_preference = move_dir_preference;
5777 new_move_dir = move_dir;
5781 MovDir[x][y] = new_move_dir;
5782 if (old_move_dir != new_move_dir)
5783 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5787 static void TurnRound(int x, int y)
5789 int direction = MovDir[x][y];
5791 int element, graphic;
5796 GfxDir[x][y] = MovDir[x][y];
5798 if (direction != MovDir[x][y])
5802 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5805 ResetGfxFrame(x, y, FALSE);
5807 element = Feld[x][y];
5808 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5810 if (graphic_info[graphic].anim_global_sync)
5811 GfxFrame[x][y] = FrameCounter;
5812 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5813 GfxFrame[x][y] = CustomValue[x][y];
5814 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5815 GfxFrame[x][y] = element_info[element].collect_score;
5816 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5817 GfxFrame[x][y] = ChangeDelay[x][y];
5821 static boolean JustBeingPushed(int x, int y)
5825 for (i = 0; i < MAX_PLAYERS; i++)
5827 struct PlayerInfo *player = &stored_player[i];
5829 if (player->active && player->is_pushing && player->MovPos)
5831 int next_jx = player->jx + (player->jx - player->last_jx);
5832 int next_jy = player->jy + (player->jy - player->last_jy);
5834 if (x == next_jx && y == next_jy)
5842 void StartMoving(int x, int y)
5844 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5845 int element = Feld[x][y];
5850 if (MovDelay[x][y] == 0)
5851 GfxAction[x][y] = ACTION_DEFAULT;
5853 if (CAN_FALL(element) && y < lev_fieldy - 1)
5855 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5856 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5857 if (JustBeingPushed(x, y))
5860 if (element == EL_QUICKSAND_FULL)
5862 if (IS_FREE(x, y + 1))
5864 InitMovingField(x, y, MV_DOWN);
5865 started_moving = TRUE;
5867 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5868 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5869 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5870 Store[x][y] = EL_ROCK;
5872 Store[x][y] = EL_ROCK;
5875 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5877 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5879 if (!MovDelay[x][y])
5880 MovDelay[x][y] = TILEY + 1;
5889 Feld[x][y] = EL_QUICKSAND_EMPTY;
5890 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5891 Store[x][y + 1] = Store[x][y];
5894 PlayLevelSoundAction(x, y, ACTION_FILLING);
5897 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5898 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5900 InitMovingField(x, y, MV_DOWN);
5901 started_moving = TRUE;
5903 Feld[x][y] = EL_QUICKSAND_FILLING;
5904 Store[x][y] = element;
5906 PlayLevelSoundAction(x, y, ACTION_FILLING);
5908 else if (element == EL_MAGIC_WALL_FULL)
5910 if (IS_FREE(x, y + 1))
5912 InitMovingField(x, y, MV_DOWN);
5913 started_moving = TRUE;
5915 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5916 Store[x][y] = EL_CHANGED(Store[x][y]);
5918 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5920 if (!MovDelay[x][y])
5921 MovDelay[x][y] = TILEY/4 + 1;
5930 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5931 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5932 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5936 else if (element == EL_BD_MAGIC_WALL_FULL)
5938 if (IS_FREE(x, y + 1))
5940 InitMovingField(x, y, MV_DOWN);
5941 started_moving = TRUE;
5943 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5944 Store[x][y] = EL_CHANGED2(Store[x][y]);
5946 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5948 if (!MovDelay[x][y])
5949 MovDelay[x][y] = TILEY/4 + 1;
5958 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5959 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5960 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5964 else if (CAN_PASS_MAGIC_WALL(element) &&
5965 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5966 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5968 InitMovingField(x, y, MV_DOWN);
5969 started_moving = TRUE;
5972 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5973 EL_BD_MAGIC_WALL_FILLING);
5974 Store[x][y] = element;
5976 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5978 SplashAcid(x, y + 1);
5980 InitMovingField(x, y, MV_DOWN);
5981 started_moving = TRUE;
5983 Store[x][y] = EL_ACID;
5985 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5986 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5988 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5989 CAN_FALL(element) && WasJustFalling[x][y] &&
5990 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5992 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5993 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5994 (Feld[x][y + 1] == EL_BLOCKED)))
5996 /* this is needed for a special case not covered by calling "Impact()"
5997 from "ContinueMoving()": if an element moves to a tile directly below
5998 another element which was just falling on that tile (which was empty
5999 in the previous frame), the falling element above would just stop
6000 instead of smashing the element below (in previous version, the above
6001 element was just checked for "moving" instead of "falling", resulting
6002 in incorrect smashes caused by horizontal movement of the above
6003 element; also, the case of the player being the element to smash was
6004 simply not covered here... :-/ ) */
6006 CheckCollision[x][y] = 0;
6010 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6012 if (MovDir[x][y] == MV_NONE)
6014 InitMovingField(x, y, MV_DOWN);
6015 started_moving = TRUE;
6018 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6020 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6021 MovDir[x][y] = MV_DOWN;
6023 InitMovingField(x, y, MV_DOWN);
6024 started_moving = TRUE;
6026 else if (element == EL_AMOEBA_DROP)
6028 Feld[x][y] = EL_AMOEBA_GROWING;
6029 Store[x][y] = EL_AMOEBA_WET;
6031 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6032 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6033 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6034 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6036 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6037 (IS_FREE(x - 1, y + 1) ||
6038 Feld[x - 1][y + 1] == EL_ACID));
6039 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6040 (IS_FREE(x + 1, y + 1) ||
6041 Feld[x + 1][y + 1] == EL_ACID));
6042 boolean can_fall_any = (can_fall_left || can_fall_right);
6043 boolean can_fall_both = (can_fall_left && can_fall_right);
6044 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6046 #if USE_NEW_ALL_SLIPPERY
6047 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6049 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6050 can_fall_right = FALSE;
6051 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6052 can_fall_left = FALSE;
6053 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6054 can_fall_right = FALSE;
6055 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6056 can_fall_left = FALSE;
6058 can_fall_any = (can_fall_left || can_fall_right);
6059 can_fall_both = FALSE;
6062 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6064 if (slippery_type == SLIPPERY_ONLY_LEFT)
6065 can_fall_right = FALSE;
6066 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6067 can_fall_left = FALSE;
6068 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6069 can_fall_right = FALSE;
6070 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6071 can_fall_left = FALSE;
6073 can_fall_any = (can_fall_left || can_fall_right);
6074 can_fall_both = (can_fall_left && can_fall_right);
6078 #if USE_NEW_ALL_SLIPPERY
6080 #if USE_NEW_SP_SLIPPERY
6081 /* !!! better use the same properties as for custom elements here !!! */
6082 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6083 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6085 can_fall_right = FALSE; /* slip down on left side */
6086 can_fall_both = FALSE;
6091 #if USE_NEW_ALL_SLIPPERY
6094 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6095 can_fall_right = FALSE; /* slip down on left side */
6097 can_fall_left = !(can_fall_right = RND(2));
6099 can_fall_both = FALSE;
6104 if (game.emulation == EMU_BOULDERDASH ||
6105 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6106 can_fall_right = FALSE; /* slip down on left side */
6108 can_fall_left = !(can_fall_right = RND(2));
6110 can_fall_both = FALSE;
6116 /* if not determined otherwise, prefer left side for slipping down */
6117 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6118 started_moving = TRUE;
6122 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6124 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6127 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6128 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6129 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6130 int belt_dir = game.belt_dir[belt_nr];
6132 if ((belt_dir == MV_LEFT && left_is_free) ||
6133 (belt_dir == MV_RIGHT && right_is_free))
6135 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6137 InitMovingField(x, y, belt_dir);
6138 started_moving = TRUE;
6140 Pushed[x][y] = TRUE;
6141 Pushed[nextx][y] = TRUE;
6143 GfxAction[x][y] = ACTION_DEFAULT;
6147 MovDir[x][y] = 0; /* if element was moving, stop it */
6152 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6154 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6156 if (CAN_MOVE(element) && !started_moving)
6159 int move_pattern = element_info[element].move_pattern;
6164 if (MovDir[x][y] == MV_NONE)
6166 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6167 x, y, element, element_info[element].token_name);
6168 printf("StartMoving(): This should never happen!\n");
6173 Moving2Blocked(x, y, &newx, &newy);
6175 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6178 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6179 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6181 WasJustMoving[x][y] = 0;
6182 CheckCollision[x][y] = 0;
6184 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6186 if (Feld[x][y] != element) /* element has changed */
6190 if (!MovDelay[x][y]) /* start new movement phase */
6192 /* all objects that can change their move direction after each step
6193 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6195 if (element != EL_YAMYAM &&
6196 element != EL_DARK_YAMYAM &&
6197 element != EL_PACMAN &&
6198 !(move_pattern & MV_ANY_DIRECTION) &&
6199 move_pattern != MV_TURNING_LEFT &&
6200 move_pattern != MV_TURNING_RIGHT &&
6201 move_pattern != MV_TURNING_LEFT_RIGHT &&
6202 move_pattern != MV_TURNING_RIGHT_LEFT &&
6203 move_pattern != MV_TURNING_RANDOM)
6207 if (MovDelay[x][y] && (element == EL_BUG ||
6208 element == EL_SPACESHIP ||
6209 element == EL_SP_SNIKSNAK ||
6210 element == EL_SP_ELECTRON ||
6211 element == EL_MOLE))
6212 DrawLevelField(x, y);
6216 if (MovDelay[x][y]) /* wait some time before next movement */
6220 if (element == EL_ROBOT ||
6221 element == EL_YAMYAM ||
6222 element == EL_DARK_YAMYAM)
6224 DrawLevelElementAnimationIfNeeded(x, y, element);
6225 PlayLevelSoundAction(x, y, ACTION_WAITING);
6227 else if (element == EL_SP_ELECTRON)
6228 DrawLevelElementAnimationIfNeeded(x, y, element);
6229 else if (element == EL_DRAGON)
6232 int dir = MovDir[x][y];
6233 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6234 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6235 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6236 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6237 dir == MV_UP ? IMG_FLAMES_1_UP :
6238 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6239 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6241 GfxAction[x][y] = ACTION_ATTACKING;
6243 if (IS_PLAYER(x, y))
6244 DrawPlayerField(x, y);
6246 DrawLevelField(x, y);
6248 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6250 for (i = 1; i <= 3; i++)
6252 int xx = x + i * dx;
6253 int yy = y + i * dy;
6254 int sx = SCREENX(xx);
6255 int sy = SCREENY(yy);
6256 int flame_graphic = graphic + (i - 1);
6258 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6263 int flamed = MovingOrBlocked2Element(xx, yy);
6267 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6269 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6270 RemoveMovingField(xx, yy);
6272 RemoveField(xx, yy);
6274 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6277 RemoveMovingField(xx, yy);
6280 ChangeDelay[xx][yy] = 0;
6282 Feld[xx][yy] = EL_FLAMES;
6284 if (IN_SCR_FIELD(sx, sy))
6286 DrawLevelFieldCrumbledSand(xx, yy);
6287 DrawGraphic(sx, sy, flame_graphic, frame);
6292 if (Feld[xx][yy] == EL_FLAMES)
6293 Feld[xx][yy] = EL_EMPTY;
6294 DrawLevelField(xx, yy);
6299 if (MovDelay[x][y]) /* element still has to wait some time */
6301 PlayLevelSoundAction(x, y, ACTION_WAITING);
6307 /* now make next step */
6309 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6311 if (DONT_COLLIDE_WITH(element) &&
6312 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6313 !PLAYER_ENEMY_PROTECTED(newx, newy))
6315 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6320 else if (CAN_MOVE_INTO_ACID(element) &&
6321 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6322 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6323 (MovDir[x][y] == MV_DOWN ||
6324 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6326 SplashAcid(newx, newy);
6327 Store[x][y] = EL_ACID;
6329 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6331 if (Feld[newx][newy] == EL_EXIT_OPEN)
6334 DrawLevelField(x, y);
6336 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6337 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6338 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6340 local_player->friends_still_needed--;
6341 if (!local_player->friends_still_needed &&
6342 !local_player->GameOver && AllPlayersGone)
6343 local_player->LevelSolved = local_player->GameOver = TRUE;
6347 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6349 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6350 DrawLevelField(newx, newy);
6352 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6354 else if (!IS_FREE(newx, newy))
6356 GfxAction[x][y] = ACTION_WAITING;
6358 if (IS_PLAYER(x, y))
6359 DrawPlayerField(x, y);
6361 DrawLevelField(x, y);
6366 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6368 if (IS_FOOD_PIG(Feld[newx][newy]))
6370 if (IS_MOVING(newx, newy))
6371 RemoveMovingField(newx, newy);
6374 Feld[newx][newy] = EL_EMPTY;
6375 DrawLevelField(newx, newy);
6378 PlayLevelSound(x, y, SND_PIG_DIGGING);
6380 else if (!IS_FREE(newx, newy))
6382 if (IS_PLAYER(x, y))
6383 DrawPlayerField(x, y);
6385 DrawLevelField(x, y);
6390 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6392 if (Store[x][y] != EL_EMPTY)
6394 boolean can_clone = FALSE;
6397 /* check if element to clone is still there */
6398 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6400 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6408 /* cannot clone or target field not free anymore -- do not clone */
6409 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6410 Store[x][y] = EL_EMPTY;
6413 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6415 if (IS_MV_DIAGONAL(MovDir[x][y]))
6417 int diagonal_move_dir = MovDir[x][y];
6418 int stored = Store[x][y];
6419 int change_delay = 8;
6422 /* android is moving diagonally */
6424 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6426 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6427 GfxElement[x][y] = EL_EMC_ANDROID;
6428 GfxAction[x][y] = ACTION_SHRINKING;
6429 GfxDir[x][y] = diagonal_move_dir;
6430 ChangeDelay[x][y] = change_delay;
6432 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6435 DrawLevelGraphicAnimation(x, y, graphic);
6436 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6438 if (Feld[newx][newy] == EL_ACID)
6440 SplashAcid(newx, newy);
6445 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6447 Store[newx][newy] = EL_EMC_ANDROID;
6448 GfxElement[newx][newy] = EL_EMC_ANDROID;
6449 GfxAction[newx][newy] = ACTION_GROWING;
6450 GfxDir[newx][newy] = diagonal_move_dir;
6451 ChangeDelay[newx][newy] = change_delay;
6453 graphic = el_act_dir2img(GfxElement[newx][newy],
6454 GfxAction[newx][newy], GfxDir[newx][newy]);
6456 DrawLevelGraphicAnimation(newx, newy, graphic);
6457 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6463 Feld[newx][newy] = EL_EMPTY;
6464 DrawLevelField(newx, newy);
6466 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6469 else if (!IS_FREE(newx, newy))
6472 if (IS_PLAYER(x, y))
6473 DrawPlayerField(x, y);
6475 DrawLevelField(x, y);
6481 else if (IS_CUSTOM_ELEMENT(element) &&
6482 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6484 int new_element = Feld[newx][newy];
6486 if (!IS_FREE(newx, newy))
6488 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6489 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6492 /* no element can dig solid indestructible elements */
6493 if (IS_INDESTRUCTIBLE(new_element) &&
6494 !IS_DIGGABLE(new_element) &&
6495 !IS_COLLECTIBLE(new_element))
6498 if (AmoebaNr[newx][newy] &&
6499 (new_element == EL_AMOEBA_FULL ||
6500 new_element == EL_BD_AMOEBA ||
6501 new_element == EL_AMOEBA_GROWING))
6503 AmoebaCnt[AmoebaNr[newx][newy]]--;
6504 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6507 if (IS_MOVING(newx, newy))
6508 RemoveMovingField(newx, newy);
6511 RemoveField(newx, newy);
6512 DrawLevelField(newx, newy);
6515 /* if digged element was about to explode, prevent the explosion */
6516 ExplodeField[newx][newy] = EX_TYPE_NONE;
6518 PlayLevelSoundAction(x, y, action);
6521 Store[newx][newy] = EL_EMPTY;
6523 /* this makes it possible to leave the removed element again */
6524 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6525 Store[newx][newy] = new_element;
6527 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6529 int move_leave_element = element_info[element].move_leave_element;
6531 /* this makes it possible to leave the removed element again */
6532 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6533 new_element : move_leave_element);
6537 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6539 RunnerVisit[x][y] = FrameCounter;
6540 PlayerVisit[x][y] /= 8; /* expire player visit path */
6543 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6545 if (!IS_FREE(newx, newy))
6547 if (IS_PLAYER(x, y))
6548 DrawPlayerField(x, y);
6550 DrawLevelField(x, y);
6556 boolean wanna_flame = !RND(10);
6557 int dx = newx - x, dy = newy - y;
6558 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6559 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6560 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6561 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6562 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6563 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6566 IS_CLASSIC_ENEMY(element1) ||
6567 IS_CLASSIC_ENEMY(element2)) &&
6568 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6569 element1 != EL_FLAMES && element2 != EL_FLAMES)
6571 ResetGfxAnimation(x, y);
6572 GfxAction[x][y] = ACTION_ATTACKING;
6574 if (IS_PLAYER(x, y))
6575 DrawPlayerField(x, y);
6577 DrawLevelField(x, y);
6579 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6581 MovDelay[x][y] = 50;
6585 RemoveField(newx, newy);
6587 Feld[newx][newy] = EL_FLAMES;
6588 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6591 RemoveField(newx1, newy1);
6593 Feld[newx1][newy1] = EL_FLAMES;
6595 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6598 RemoveField(newx2, newy2);
6600 Feld[newx2][newy2] = EL_FLAMES;
6607 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6608 Feld[newx][newy] == EL_DIAMOND)
6610 if (IS_MOVING(newx, newy))
6611 RemoveMovingField(newx, newy);
6614 Feld[newx][newy] = EL_EMPTY;
6615 DrawLevelField(newx, newy);
6618 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6620 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6621 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6623 if (AmoebaNr[newx][newy])
6625 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6626 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6627 Feld[newx][newy] == EL_BD_AMOEBA)
6628 AmoebaCnt[AmoebaNr[newx][newy]]--;
6633 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6635 RemoveMovingField(newx, newy);
6638 if (IS_MOVING(newx, newy))
6640 RemoveMovingField(newx, newy);
6645 Feld[newx][newy] = EL_EMPTY;
6646 DrawLevelField(newx, newy);
6649 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6651 else if ((element == EL_PACMAN || element == EL_MOLE)
6652 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6654 if (AmoebaNr[newx][newy])
6656 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6657 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6658 Feld[newx][newy] == EL_BD_AMOEBA)
6659 AmoebaCnt[AmoebaNr[newx][newy]]--;
6662 if (element == EL_MOLE)
6664 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6665 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6667 ResetGfxAnimation(x, y);
6668 GfxAction[x][y] = ACTION_DIGGING;
6669 DrawLevelField(x, y);
6671 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6673 return; /* wait for shrinking amoeba */
6675 else /* element == EL_PACMAN */
6677 Feld[newx][newy] = EL_EMPTY;
6678 DrawLevelField(newx, newy);
6679 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6682 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6683 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6684 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6686 /* wait for shrinking amoeba to completely disappear */
6689 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6691 /* object was running against a wall */
6696 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6697 if (move_pattern & MV_ANY_DIRECTION &&
6698 move_pattern == MovDir[x][y])
6700 int blocking_element =
6701 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6703 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6706 element = Feld[x][y]; /* element might have changed */
6710 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6711 DrawLevelElementAnimation(x, y, element);
6713 if (DONT_TOUCH(element))
6714 TestIfBadThingTouchesPlayer(x, y);
6719 InitMovingField(x, y, MovDir[x][y]);
6721 PlayLevelSoundAction(x, y, ACTION_MOVING);
6725 ContinueMoving(x, y);
6728 void ContinueMoving(int x, int y)
6730 int element = Feld[x][y];
6731 struct ElementInfo *ei = &element_info[element];
6732 int direction = MovDir[x][y];
6733 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6734 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6735 int newx = x + dx, newy = y + dy;
6736 int stored = Store[x][y];
6737 int stored_new = Store[newx][newy];
6738 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6739 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6740 boolean last_line = (newy == lev_fieldy - 1);
6742 MovPos[x][y] += getElementMoveStepsize(x, y);
6744 if (pushed_by_player) /* special case: moving object pushed by player */
6745 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6747 if (ABS(MovPos[x][y]) < TILEX)
6749 DrawLevelField(x, y);
6751 return; /* element is still moving */
6754 /* element reached destination field */
6756 Feld[x][y] = EL_EMPTY;
6757 Feld[newx][newy] = element;
6758 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6760 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6762 element = Feld[newx][newy] = EL_ACID;
6764 else if (element == EL_MOLE)
6766 Feld[x][y] = EL_SAND;
6768 DrawLevelFieldCrumbledSandNeighbours(x, y);
6770 else if (element == EL_QUICKSAND_FILLING)
6772 element = Feld[newx][newy] = get_next_element(element);
6773 Store[newx][newy] = Store[x][y];
6775 else if (element == EL_QUICKSAND_EMPTYING)
6777 Feld[x][y] = get_next_element(element);
6778 element = Feld[newx][newy] = Store[x][y];
6780 else if (element == EL_MAGIC_WALL_FILLING)
6782 element = Feld[newx][newy] = get_next_element(element);
6783 if (!game.magic_wall_active)
6784 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6785 Store[newx][newy] = Store[x][y];
6787 else if (element == EL_MAGIC_WALL_EMPTYING)
6789 Feld[x][y] = get_next_element(element);
6790 if (!game.magic_wall_active)
6791 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6792 element = Feld[newx][newy] = Store[x][y];
6794 #if USE_NEW_CUSTOM_VALUE
6795 InitField(newx, newy, FALSE);
6798 else if (element == EL_BD_MAGIC_WALL_FILLING)
6800 element = Feld[newx][newy] = get_next_element(element);
6801 if (!game.magic_wall_active)
6802 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6803 Store[newx][newy] = Store[x][y];
6805 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6807 Feld[x][y] = get_next_element(element);
6808 if (!game.magic_wall_active)
6809 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6810 element = Feld[newx][newy] = Store[x][y];
6812 #if USE_NEW_CUSTOM_VALUE
6813 InitField(newx, newy, FALSE);
6816 else if (element == EL_AMOEBA_DROPPING)
6818 Feld[x][y] = get_next_element(element);
6819 element = Feld[newx][newy] = Store[x][y];
6821 else if (element == EL_SOKOBAN_OBJECT)
6824 Feld[x][y] = Back[x][y];
6826 if (Back[newx][newy])
6827 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6829 Back[x][y] = Back[newx][newy] = 0;
6832 Store[x][y] = EL_EMPTY;
6837 MovDelay[newx][newy] = 0;
6840 if (CAN_CHANGE_OR_HAS_ACTION(element))
6842 if (CAN_CHANGE(element))
6845 /* copy element change control values to new field */
6846 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6847 ChangePage[newx][newy] = ChangePage[x][y];
6848 ChangeCount[newx][newy] = ChangeCount[x][y];
6849 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6852 #if USE_NEW_CUSTOM_VALUE
6853 CustomValue[newx][newy] = CustomValue[x][y];
6859 #if USE_NEW_CUSTOM_VALUE
6860 CustomValue[newx][newy] = CustomValue[x][y];
6864 ChangeDelay[x][y] = 0;
6865 ChangePage[x][y] = -1;
6866 ChangeCount[x][y] = 0;
6867 ChangeEvent[x][y] = -1;
6869 #if USE_NEW_CUSTOM_VALUE
6870 CustomValue[x][y] = 0;
6873 /* copy animation control values to new field */
6874 GfxFrame[newx][newy] = GfxFrame[x][y];
6875 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6876 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6877 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6879 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6881 /* some elements can leave other elements behind after moving */
6883 if (ei->move_leave_element != EL_EMPTY &&
6884 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6885 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6887 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6888 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6889 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6892 int move_leave_element = ei->move_leave_element;
6896 /* this makes it possible to leave the removed element again */
6897 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6898 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6900 /* this makes it possible to leave the removed element again */
6901 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6902 move_leave_element = stored;
6905 /* this makes it possible to leave the removed element again */
6906 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6907 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6908 move_leave_element = stored;
6911 Feld[x][y] = move_leave_element;
6913 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6914 MovDir[x][y] = direction;
6916 InitField(x, y, FALSE);
6918 if (GFX_CRUMBLED(Feld[x][y]))
6919 DrawLevelFieldCrumbledSandNeighbours(x, y);
6921 if (ELEM_IS_PLAYER(move_leave_element))
6922 RelocatePlayer(x, y, move_leave_element);
6925 /* do this after checking for left-behind element */
6926 ResetGfxAnimation(x, y); /* reset animation values for old field */
6928 if (!CAN_MOVE(element) ||
6929 (CAN_FALL(element) && direction == MV_DOWN &&
6930 (element == EL_SPRING ||
6931 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6932 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6933 GfxDir[x][y] = MovDir[newx][newy] = 0;
6935 DrawLevelField(x, y);
6936 DrawLevelField(newx, newy);
6938 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6940 /* prevent pushed element from moving on in pushed direction */
6941 if (pushed_by_player && CAN_MOVE(element) &&
6942 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6943 !(element_info[element].move_pattern & direction))
6944 TurnRound(newx, newy);
6946 /* prevent elements on conveyor belt from moving on in last direction */
6947 if (pushed_by_conveyor && CAN_FALL(element) &&
6948 direction & MV_HORIZONTAL)
6949 MovDir[newx][newy] = 0;
6951 if (!pushed_by_player)
6953 int nextx = newx + dx, nexty = newy + dy;
6954 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6956 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6958 if (CAN_FALL(element) && direction == MV_DOWN)
6959 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6961 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6962 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6965 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6967 TestIfBadThingTouchesPlayer(newx, newy);
6968 TestIfBadThingTouchesFriend(newx, newy);
6970 if (!IS_CUSTOM_ELEMENT(element))
6971 TestIfBadThingTouchesOtherBadThing(newx, newy);
6973 else if (element == EL_PENGUIN)
6974 TestIfFriendTouchesBadThing(newx, newy);
6976 /* give the player one last chance (one more frame) to move away */
6977 if (CAN_FALL(element) && direction == MV_DOWN &&
6978 (last_line || (!IS_FREE(x, newy + 1) &&
6979 (!IS_PLAYER(x, newy + 1) ||
6980 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6983 if (pushed_by_player && !game.use_change_when_pushing_bug)
6985 int push_side = MV_DIR_OPPOSITE(direction);
6986 struct PlayerInfo *player = PLAYERINFO(x, y);
6988 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6989 player->index_bit, push_side);
6990 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6991 player->index_bit, push_side);
6994 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6995 MovDelay[newx][newy] = 1;
6997 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6999 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7002 if (ChangePage[newx][newy] != -1) /* delayed change */
7004 int page = ChangePage[newx][newy];
7005 struct ElementChangeInfo *change = &ei->change_page[page];
7007 ChangePage[newx][newy] = -1;
7009 if (change->can_change)
7011 if (ChangeElement(newx, newy, element, page))
7013 if (change->post_change_function)
7014 change->post_change_function(newx, newy);
7018 if (change->has_action)
7019 ExecuteCustomElementAction(newx, newy, element, page);
7023 TestIfElementHitsCustomElement(newx, newy, direction);
7024 TestIfPlayerTouchesCustomElement(newx, newy);
7025 TestIfElementTouchesCustomElement(newx, newy);
7028 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7029 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7030 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7031 MV_DIR_OPPOSITE(direction));
7035 int AmoebeNachbarNr(int ax, int ay)
7038 int element = Feld[ax][ay];
7040 static int xy[4][2] =
7048 for (i = 0; i < NUM_DIRECTIONS; i++)
7050 int x = ax + xy[i][0];
7051 int y = ay + xy[i][1];
7053 if (!IN_LEV_FIELD(x, y))
7056 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7057 group_nr = AmoebaNr[x][y];
7063 void AmoebenVereinigen(int ax, int ay)
7065 int i, x, y, xx, yy;
7066 int new_group_nr = AmoebaNr[ax][ay];
7067 static int xy[4][2] =
7075 if (new_group_nr == 0)
7078 for (i = 0; i < NUM_DIRECTIONS; i++)
7083 if (!IN_LEV_FIELD(x, y))
7086 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7087 Feld[x][y] == EL_BD_AMOEBA ||
7088 Feld[x][y] == EL_AMOEBA_DEAD) &&
7089 AmoebaNr[x][y] != new_group_nr)
7091 int old_group_nr = AmoebaNr[x][y];
7093 if (old_group_nr == 0)
7096 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7097 AmoebaCnt[old_group_nr] = 0;
7098 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7099 AmoebaCnt2[old_group_nr] = 0;
7102 SCAN_PLAYFIELD(xx, yy)
7104 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7107 if (AmoebaNr[xx][yy] == old_group_nr)
7108 AmoebaNr[xx][yy] = new_group_nr;
7114 void AmoebeUmwandeln(int ax, int ay)
7118 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7120 int group_nr = AmoebaNr[ax][ay];
7125 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7126 printf("AmoebeUmwandeln(): This should never happen!\n");
7132 SCAN_PLAYFIELD(x, y)
7134 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7137 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7140 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7144 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7145 SND_AMOEBA_TURNING_TO_GEM :
7146 SND_AMOEBA_TURNING_TO_ROCK));
7151 static int xy[4][2] =
7159 for (i = 0; i < NUM_DIRECTIONS; i++)
7164 if (!IN_LEV_FIELD(x, y))
7167 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7169 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7170 SND_AMOEBA_TURNING_TO_GEM :
7171 SND_AMOEBA_TURNING_TO_ROCK));
7178 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7181 int group_nr = AmoebaNr[ax][ay];
7182 boolean done = FALSE;
7187 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7188 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7194 SCAN_PLAYFIELD(x, y)
7196 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7199 if (AmoebaNr[x][y] == group_nr &&
7200 (Feld[x][y] == EL_AMOEBA_DEAD ||
7201 Feld[x][y] == EL_BD_AMOEBA ||
7202 Feld[x][y] == EL_AMOEBA_GROWING))
7205 Feld[x][y] = new_element;
7206 InitField(x, y, FALSE);
7207 DrawLevelField(x, y);
7213 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7214 SND_BD_AMOEBA_TURNING_TO_ROCK :
7215 SND_BD_AMOEBA_TURNING_TO_GEM));
7218 void AmoebeWaechst(int x, int y)
7220 static unsigned long sound_delay = 0;
7221 static unsigned long sound_delay_value = 0;
7223 if (!MovDelay[x][y]) /* start new growing cycle */
7227 if (DelayReached(&sound_delay, sound_delay_value))
7229 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7230 sound_delay_value = 30;
7234 if (MovDelay[x][y]) /* wait some time before growing bigger */
7237 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7239 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7240 6 - MovDelay[x][y]);
7242 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7245 if (!MovDelay[x][y])
7247 Feld[x][y] = Store[x][y];
7249 DrawLevelField(x, y);
7254 void AmoebaDisappearing(int x, int y)
7256 static unsigned long sound_delay = 0;
7257 static unsigned long sound_delay_value = 0;
7259 if (!MovDelay[x][y]) /* start new shrinking cycle */
7263 if (DelayReached(&sound_delay, sound_delay_value))
7264 sound_delay_value = 30;
7267 if (MovDelay[x][y]) /* wait some time before shrinking */
7270 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7272 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7273 6 - MovDelay[x][y]);
7275 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7278 if (!MovDelay[x][y])
7280 Feld[x][y] = EL_EMPTY;
7281 DrawLevelField(x, y);
7283 /* don't let mole enter this field in this cycle;
7284 (give priority to objects falling to this field from above) */
7290 void AmoebeAbleger(int ax, int ay)
7293 int element = Feld[ax][ay];
7294 int graphic = el2img(element);
7295 int newax = ax, neway = ay;
7296 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7297 static int xy[4][2] =
7305 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7307 Feld[ax][ay] = EL_AMOEBA_DEAD;
7308 DrawLevelField(ax, ay);
7312 if (IS_ANIMATED(graphic))
7313 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7315 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7316 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7318 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7321 if (MovDelay[ax][ay])
7325 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7328 int x = ax + xy[start][0];
7329 int y = ay + xy[start][1];
7331 if (!IN_LEV_FIELD(x, y))
7334 if (IS_FREE(x, y) ||
7335 CAN_GROW_INTO(Feld[x][y]) ||
7336 Feld[x][y] == EL_QUICKSAND_EMPTY)
7342 if (newax == ax && neway == ay)
7345 else /* normal or "filled" (BD style) amoeba */
7348 boolean waiting_for_player = FALSE;
7350 for (i = 0; i < NUM_DIRECTIONS; i++)
7352 int j = (start + i) % 4;
7353 int x = ax + xy[j][0];
7354 int y = ay + xy[j][1];
7356 if (!IN_LEV_FIELD(x, y))
7359 if (IS_FREE(x, y) ||
7360 CAN_GROW_INTO(Feld[x][y]) ||
7361 Feld[x][y] == EL_QUICKSAND_EMPTY)
7367 else if (IS_PLAYER(x, y))
7368 waiting_for_player = TRUE;
7371 if (newax == ax && neway == ay) /* amoeba cannot grow */
7373 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7375 Feld[ax][ay] = EL_AMOEBA_DEAD;
7376 DrawLevelField(ax, ay);
7377 AmoebaCnt[AmoebaNr[ax][ay]]--;
7379 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7381 if (element == EL_AMOEBA_FULL)
7382 AmoebeUmwandeln(ax, ay);
7383 else if (element == EL_BD_AMOEBA)
7384 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7389 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7391 /* amoeba gets larger by growing in some direction */
7393 int new_group_nr = AmoebaNr[ax][ay];
7396 if (new_group_nr == 0)
7398 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7399 printf("AmoebeAbleger(): This should never happen!\n");
7404 AmoebaNr[newax][neway] = new_group_nr;
7405 AmoebaCnt[new_group_nr]++;
7406 AmoebaCnt2[new_group_nr]++;
7408 /* if amoeba touches other amoeba(s) after growing, unify them */
7409 AmoebenVereinigen(newax, neway);
7411 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7413 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7419 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7420 (neway == lev_fieldy - 1 && newax != ax))
7422 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7423 Store[newax][neway] = element;
7425 else if (neway == ay || element == EL_EMC_DRIPPER)
7427 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7429 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7433 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7434 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7435 Store[ax][ay] = EL_AMOEBA_DROP;
7436 ContinueMoving(ax, ay);
7440 DrawLevelField(newax, neway);
7443 void Life(int ax, int ay)
7447 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7450 int element = Feld[ax][ay];
7451 int graphic = el2img(element);
7452 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7454 boolean changed = FALSE;
7456 if (IS_ANIMATED(graphic))
7457 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7462 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7463 MovDelay[ax][ay] = life_time;
7465 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7468 if (MovDelay[ax][ay])
7472 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7474 int xx = ax+x1, yy = ay+y1;
7477 if (!IN_LEV_FIELD(xx, yy))
7480 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7482 int x = xx+x2, y = yy+y2;
7484 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7487 if (((Feld[x][y] == element ||
7488 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7490 (IS_FREE(x, y) && Stop[x][y]))
7494 if (xx == ax && yy == ay) /* field in the middle */
7496 if (nachbarn < life_parameter[0] ||
7497 nachbarn > life_parameter[1])
7499 Feld[xx][yy] = EL_EMPTY;
7501 DrawLevelField(xx, yy);
7502 Stop[xx][yy] = TRUE;
7506 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7507 { /* free border field */
7508 if (nachbarn >= life_parameter[2] &&
7509 nachbarn <= life_parameter[3])
7511 Feld[xx][yy] = element;
7512 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7514 DrawLevelField(xx, yy);
7515 Stop[xx][yy] = TRUE;
7522 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7523 SND_GAME_OF_LIFE_GROWING);
7526 static void InitRobotWheel(int x, int y)
7528 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7531 static void RunRobotWheel(int x, int y)
7533 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7536 static void StopRobotWheel(int x, int y)
7538 if (ZX == x && ZY == y)
7542 static void InitTimegateWheel(int x, int y)
7544 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7547 static void RunTimegateWheel(int x, int y)
7549 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7552 static void InitMagicBallDelay(int x, int y)
7555 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7557 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7561 static void ActivateMagicBall(int bx, int by)
7565 if (level.ball_random)
7567 int pos_border = RND(8); /* select one of the eight border elements */
7568 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7569 int xx = pos_content % 3;
7570 int yy = pos_content / 3;
7575 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7576 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7580 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7582 int xx = x - bx + 1;
7583 int yy = y - by + 1;
7585 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7586 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7590 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7593 static void InitDiagonalMovingElement(int x, int y)
7596 MovDelay[x][y] = level.android_move_time;
7600 void CheckExit(int x, int y)
7602 if (local_player->gems_still_needed > 0 ||
7603 local_player->sokobanfields_still_needed > 0 ||
7604 local_player->lights_still_needed > 0)
7606 int element = Feld[x][y];
7607 int graphic = el2img(element);
7609 if (IS_ANIMATED(graphic))
7610 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7615 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7618 Feld[x][y] = EL_EXIT_OPENING;
7620 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7623 void CheckExitSP(int x, int y)
7625 if (local_player->gems_still_needed > 0)
7627 int element = Feld[x][y];
7628 int graphic = el2img(element);
7630 if (IS_ANIMATED(graphic))
7631 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7636 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7639 Feld[x][y] = EL_SP_EXIT_OPENING;
7641 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7644 static void CloseAllOpenTimegates()
7649 SCAN_PLAYFIELD(x, y)
7651 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7654 int element = Feld[x][y];
7656 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7658 Feld[x][y] = EL_TIMEGATE_CLOSING;
7660 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7665 void EdelsteinFunkeln(int x, int y)
7667 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7670 if (Feld[x][y] == EL_BD_DIAMOND)
7673 if (MovDelay[x][y] == 0) /* next animation frame */
7674 MovDelay[x][y] = 11 * !SimpleRND(500);
7676 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7680 if (setup.direct_draw && MovDelay[x][y])
7681 SetDrawtoField(DRAW_BUFFERED);
7683 DrawLevelElementAnimation(x, y, Feld[x][y]);
7685 if (MovDelay[x][y] != 0)
7687 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7688 10 - MovDelay[x][y]);
7690 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7692 if (setup.direct_draw)
7696 dest_x = FX + SCREENX(x) * TILEX;
7697 dest_y = FY + SCREENY(y) * TILEY;
7699 BlitBitmap(drawto_field, window,
7700 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7701 SetDrawtoField(DRAW_DIRECT);
7707 void MauerWaechst(int x, int y)
7711 if (!MovDelay[x][y]) /* next animation frame */
7712 MovDelay[x][y] = 3 * delay;
7714 if (MovDelay[x][y]) /* wait some time before next frame */
7718 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7720 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7721 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7723 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7726 if (!MovDelay[x][y])
7728 if (MovDir[x][y] == MV_LEFT)
7730 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7731 DrawLevelField(x - 1, y);
7733 else if (MovDir[x][y] == MV_RIGHT)
7735 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7736 DrawLevelField(x + 1, y);
7738 else if (MovDir[x][y] == MV_UP)
7740 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7741 DrawLevelField(x, y - 1);
7745 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7746 DrawLevelField(x, y + 1);
7749 Feld[x][y] = Store[x][y];
7751 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7752 DrawLevelField(x, y);
7757 void MauerAbleger(int ax, int ay)
7759 int element = Feld[ax][ay];
7760 int graphic = el2img(element);
7761 boolean oben_frei = FALSE, unten_frei = FALSE;
7762 boolean links_frei = FALSE, rechts_frei = FALSE;
7763 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7764 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7765 boolean new_wall = FALSE;
7767 if (IS_ANIMATED(graphic))
7768 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7770 if (!MovDelay[ax][ay]) /* start building new wall */
7771 MovDelay[ax][ay] = 6;
7773 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7776 if (MovDelay[ax][ay])
7780 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7782 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7784 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7786 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7789 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7790 element == EL_EXPANDABLE_WALL_ANY)
7794 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7795 Store[ax][ay-1] = element;
7796 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7797 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7798 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7799 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7804 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7805 Store[ax][ay+1] = element;
7806 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7807 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7808 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7809 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7814 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7815 element == EL_EXPANDABLE_WALL_ANY ||
7816 element == EL_EXPANDABLE_WALL ||
7817 element == EL_BD_EXPANDABLE_WALL)
7821 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7822 Store[ax-1][ay] = element;
7823 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7824 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7825 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7826 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7832 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7833 Store[ax+1][ay] = element;
7834 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7835 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7836 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7837 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7842 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7843 DrawLevelField(ax, ay);
7845 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7847 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7848 unten_massiv = TRUE;
7849 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7850 links_massiv = TRUE;
7851 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7852 rechts_massiv = TRUE;
7854 if (((oben_massiv && unten_massiv) ||
7855 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7856 element == EL_EXPANDABLE_WALL) &&
7857 ((links_massiv && rechts_massiv) ||
7858 element == EL_EXPANDABLE_WALL_VERTICAL))
7859 Feld[ax][ay] = EL_WALL;
7862 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7865 void CheckForDragon(int x, int y)
7868 boolean dragon_found = FALSE;
7869 static int xy[4][2] =
7877 for (i = 0; i < NUM_DIRECTIONS; i++)
7879 for (j = 0; j < 4; j++)
7881 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7883 if (IN_LEV_FIELD(xx, yy) &&
7884 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7886 if (Feld[xx][yy] == EL_DRAGON)
7887 dragon_found = TRUE;
7896 for (i = 0; i < NUM_DIRECTIONS; i++)
7898 for (j = 0; j < 3; j++)
7900 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7902 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7904 Feld[xx][yy] = EL_EMPTY;
7905 DrawLevelField(xx, yy);
7914 static void InitBuggyBase(int x, int y)
7916 int element = Feld[x][y];
7917 int activating_delay = FRAMES_PER_SECOND / 4;
7920 (element == EL_SP_BUGGY_BASE ?
7921 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7922 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7924 element == EL_SP_BUGGY_BASE_ACTIVE ?
7925 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7928 static void WarnBuggyBase(int x, int y)
7931 static int xy[4][2] =
7939 for (i = 0; i < NUM_DIRECTIONS; i++)
7941 int xx = x + xy[i][0];
7942 int yy = y + xy[i][1];
7944 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7946 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7953 static void InitTrap(int x, int y)
7955 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7958 static void ActivateTrap(int x, int y)
7960 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7963 static void ChangeActiveTrap(int x, int y)
7965 int graphic = IMG_TRAP_ACTIVE;
7967 /* if new animation frame was drawn, correct crumbled sand border */
7968 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7969 DrawLevelFieldCrumbledSand(x, y);
7972 static int getSpecialActionElement(int element, int number, int base_element)
7974 return (element != EL_EMPTY ? element :
7975 number != -1 ? base_element + number - 1 :
7979 static int getModifiedActionNumber(int value_old, int operator, int operand,
7980 int value_min, int value_max)
7982 int value_new = (operator == CA_MODE_SET ? operand :
7983 operator == CA_MODE_ADD ? value_old + operand :
7984 operator == CA_MODE_SUBTRACT ? value_old - operand :
7985 operator == CA_MODE_MULTIPLY ? value_old * operand :
7986 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7987 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7990 return (value_new < value_min ? value_min :
7991 value_new > value_max ? value_max :
7995 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7997 struct ElementInfo *ei = &element_info[element];
7998 struct ElementChangeInfo *change = &ei->change_page[page];
7999 int target_element = change->target_element;
8000 int action_type = change->action_type;
8001 int action_mode = change->action_mode;
8002 int action_arg = change->action_arg;
8005 if (!change->has_action)
8008 /* ---------- determine action paramater values -------------------------- */
8010 int level_time_value =
8011 (level.time > 0 ? TimeLeft :
8014 int action_arg_element =
8015 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8016 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8017 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8020 int action_arg_direction =
8021 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8022 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8023 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8024 change->actual_trigger_side :
8025 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8026 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8029 int action_arg_number_min =
8030 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8033 int action_arg_number_max =
8034 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8035 action_type == CA_SET_LEVEL_GEMS ? 999 :
8036 action_type == CA_SET_LEVEL_TIME ? 9999 :
8037 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8038 action_type == CA_SET_CE_VALUE ? 9999 :
8039 action_type == CA_SET_CE_SCORE ? 9999 :
8042 int action_arg_number_reset =
8043 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8044 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8045 action_type == CA_SET_LEVEL_TIME ? level.time :
8046 action_type == CA_SET_LEVEL_SCORE ? 0 :
8048 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8050 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8052 action_type == CA_SET_CE_SCORE ? 0 :
8055 int action_arg_number =
8056 (action_arg <= CA_ARG_MAX ? action_arg :
8057 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8058 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8059 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8060 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8061 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8062 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8063 #if USE_NEW_CUSTOM_VALUE
8064 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8066 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8068 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8069 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8070 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8071 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8072 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8073 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8074 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8075 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8076 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8077 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8078 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8081 int action_arg_number_old =
8082 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8083 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8084 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8085 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8086 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8089 int action_arg_number_new =
8090 getModifiedActionNumber(action_arg_number_old,
8091 action_mode, action_arg_number,
8092 action_arg_number_min, action_arg_number_max);
8094 int trigger_player_bits =
8095 (change->actual_trigger_player >= EL_PLAYER_1 &&
8096 change->actual_trigger_player <= EL_PLAYER_4 ?
8097 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8100 int action_arg_player_bits =
8101 (action_arg >= CA_ARG_PLAYER_1 &&
8102 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8103 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8106 /* ---------- execute action -------------------------------------------- */
8115 /* ---------- level actions ------------------------------------------- */
8117 case CA_RESTART_LEVEL:
8119 game.restart_level = TRUE;
8124 case CA_SHOW_ENVELOPE:
8126 int element = getSpecialActionElement(action_arg_element,
8127 action_arg_number, EL_ENVELOPE_1);
8129 if (IS_ENVELOPE(element))
8130 local_player->show_envelope = element;
8135 case CA_SET_LEVEL_TIME:
8137 if (level.time > 0) /* only modify limited time value */
8139 TimeLeft = action_arg_number_new;
8141 DrawGameValue_Time(TimeLeft);
8143 if (!TimeLeft && setup.time_limit)
8144 for (i = 0; i < MAX_PLAYERS; i++)
8145 KillPlayer(&stored_player[i]);
8151 case CA_SET_LEVEL_SCORE:
8153 local_player->score = action_arg_number_new;
8155 DrawGameValue_Score(local_player->score);
8160 case CA_SET_LEVEL_GEMS:
8162 local_player->gems_still_needed = action_arg_number_new;
8164 DrawGameValue_Emeralds(local_player->gems_still_needed);
8169 #if !USE_PLAYER_GRAVITY
8170 case CA_SET_LEVEL_GRAVITY:
8172 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8173 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8174 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8180 case CA_SET_LEVEL_WIND:
8182 game.wind_direction = action_arg_direction;
8187 /* ---------- player actions ------------------------------------------ */
8189 case CA_MOVE_PLAYER:
8191 /* automatically move to the next field in specified direction */
8192 for (i = 0; i < MAX_PLAYERS; i++)
8193 if (trigger_player_bits & (1 << i))
8194 stored_player[i].programmed_action = action_arg_direction;
8199 case CA_EXIT_PLAYER:
8201 for (i = 0; i < MAX_PLAYERS; i++)
8202 if (action_arg_player_bits & (1 << i))
8203 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8208 case CA_KILL_PLAYER:
8210 for (i = 0; i < MAX_PLAYERS; i++)
8211 if (action_arg_player_bits & (1 << i))
8212 KillPlayer(&stored_player[i]);
8217 case CA_SET_PLAYER_KEYS:
8219 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8220 int element = getSpecialActionElement(action_arg_element,
8221 action_arg_number, EL_KEY_1);
8223 if (IS_KEY(element))
8225 for (i = 0; i < MAX_PLAYERS; i++)
8227 if (trigger_player_bits & (1 << i))
8229 stored_player[i].key[KEY_NR(element)] = key_state;
8232 DrawGameDoorValues();
8234 DrawGameValue_Keys(stored_player[i].key);
8237 redraw_mask |= REDRAW_DOOR_1;
8245 case CA_SET_PLAYER_SPEED:
8247 for (i = 0; i < MAX_PLAYERS; i++)
8249 if (trigger_player_bits & (1 << i))
8251 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8253 if (action_arg == CA_ARG_SPEED_FASTER &&
8254 stored_player[i].cannot_move)
8256 action_arg_number = STEPSIZE_VERY_SLOW;
8258 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8259 action_arg == CA_ARG_SPEED_FASTER)
8261 action_arg_number = 2;
8262 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8265 else if (action_arg == CA_ARG_NUMBER_RESET)
8267 action_arg_number = level.initial_player_stepsize[i];
8271 getModifiedActionNumber(move_stepsize,
8274 action_arg_number_min,
8275 action_arg_number_max);
8278 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8280 /* make sure that value is power of 2 */
8281 move_stepsize = (1 << log_2(move_stepsize));
8283 /* do no immediately change -- the player might just be moving */
8284 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8286 stored_player[i].cannot_move =
8287 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8295 case CA_SET_PLAYER_SHIELD:
8297 for (i = 0; i < MAX_PLAYERS; i++)
8299 if (trigger_player_bits & (1 << i))
8301 if (action_arg == CA_ARG_SHIELD_OFF)
8303 stored_player[i].shield_normal_time_left = 0;
8304 stored_player[i].shield_deadly_time_left = 0;
8306 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8308 stored_player[i].shield_normal_time_left = 999999;
8310 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8312 stored_player[i].shield_normal_time_left = 999999;
8313 stored_player[i].shield_deadly_time_left = 999999;
8321 #if USE_PLAYER_GRAVITY
8322 case CA_SET_PLAYER_GRAVITY:
8324 for (i = 0; i < MAX_PLAYERS; i++)
8326 if (trigger_player_bits & (1 << i))
8328 stored_player[i].gravity =
8329 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8330 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8331 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8332 stored_player[i].gravity);
8340 case CA_SET_PLAYER_ARTWORK:
8342 for (i = 0; i < MAX_PLAYERS; i++)
8344 if (trigger_player_bits & (1 << i))
8346 int artwork_element = action_arg_element;
8348 if (action_arg == CA_ARG_ELEMENT_RESET)
8350 (level.use_artwork_element[i] ? level.artwork_element[i] :
8351 stored_player[i].element_nr);
8353 stored_player[i].artwork_element = artwork_element;
8355 SetPlayerWaiting(&stored_player[i], FALSE);
8357 /* set number of special actions for bored and sleeping animation */
8358 stored_player[i].num_special_action_bored =
8359 get_num_special_action(artwork_element,
8360 ACTION_BORING_1, ACTION_BORING_LAST);
8361 stored_player[i].num_special_action_sleeping =
8362 get_num_special_action(artwork_element,
8363 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8370 /* ---------- CE actions ---------------------------------------------- */
8372 case CA_SET_CE_VALUE:
8374 #if USE_NEW_CUSTOM_VALUE
8375 int last_ce_value = CustomValue[x][y];
8377 CustomValue[x][y] = action_arg_number_new;
8380 printf("::: CE value == %d\n", CustomValue[x][y]);
8383 if (CustomValue[x][y] != last_ce_value)
8385 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8386 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8388 if (CustomValue[x][y] == 0)
8391 printf("::: CE_VALUE_GETS_ZERO\n");
8394 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8395 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8398 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8408 case CA_SET_CE_SCORE:
8410 #if USE_NEW_CUSTOM_VALUE
8411 int last_ce_score = ei->collect_score;
8413 ei->collect_score = action_arg_number_new;
8416 printf("::: CE score == %d\n", ei->collect_score);
8419 if (ei->collect_score != last_ce_score)
8421 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8422 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8424 if (ei->collect_score == 0)
8429 printf("::: CE_SCORE_GETS_ZERO\n");
8432 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8433 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8436 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8441 This is a very special case that seems to be a mixture between
8442 CheckElementChange() and CheckTriggeredElementChange(): while
8443 the first one only affects single elements that are triggered
8444 directly, the second one affects multiple elements in the playfield
8445 that are triggered indirectly by another element. This is a third
8446 case: Changing the CE score always affects multiple identical CEs,
8447 so every affected CE must be checked, not only the single CE for
8448 which the CE score was changed in the first place (as every instance
8449 of that CE shares the same CE score, and therefore also can change)!
8451 SCAN_PLAYFIELD(xx, yy)
8453 if (Feld[xx][yy] == element)
8454 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8455 CE_SCORE_GETS_ZERO);
8466 /* ---------- engine actions ------------------------------------------ */
8468 case CA_SET_ENGINE_SCAN_MODE:
8470 InitPlayfieldScanMode(action_arg);
8480 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8482 int old_element = Feld[x][y];
8483 int new_element = get_element_from_group_element(element);
8484 int previous_move_direction = MovDir[x][y];
8485 #if USE_NEW_CUSTOM_VALUE
8486 int last_ce_value = CustomValue[x][y];
8488 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8489 boolean add_player_onto_element = (new_element_is_player &&
8490 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8491 /* this breaks SnakeBite when a snake is
8492 halfway through a door that closes */
8493 /* NOW FIXED AT LEVEL INIT IN files.c */
8494 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8496 IS_WALKABLE(old_element));
8499 /* check if element under the player changes from accessible to unaccessible
8500 (needed for special case of dropping element which then changes) */
8501 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8502 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8510 if (!add_player_onto_element)
8512 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8513 RemoveMovingField(x, y);
8517 Feld[x][y] = new_element;
8519 #if !USE_GFX_RESET_GFX_ANIMATION
8520 ResetGfxAnimation(x, y);
8521 ResetRandomAnimationValue(x, y);
8524 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8525 MovDir[x][y] = previous_move_direction;
8527 #if USE_NEW_CUSTOM_VALUE
8528 if (element_info[new_element].use_last_ce_value)
8529 CustomValue[x][y] = last_ce_value;
8532 InitField_WithBug1(x, y, FALSE);
8534 new_element = Feld[x][y]; /* element may have changed */
8536 #if USE_GFX_RESET_GFX_ANIMATION
8537 ResetGfxAnimation(x, y);
8538 ResetRandomAnimationValue(x, y);
8541 DrawLevelField(x, y);
8543 if (GFX_CRUMBLED(new_element))
8544 DrawLevelFieldCrumbledSandNeighbours(x, y);
8548 /* check if element under the player changes from accessible to unaccessible
8549 (needed for special case of dropping element which then changes) */
8550 /* (must be checked after creating new element for walkable group elements) */
8551 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8552 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8560 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8561 if (new_element_is_player)
8562 RelocatePlayer(x, y, new_element);
8565 ChangeCount[x][y]++; /* count number of changes in the same frame */
8567 TestIfBadThingTouchesPlayer(x, y);
8568 TestIfPlayerTouchesCustomElement(x, y);
8569 TestIfElementTouchesCustomElement(x, y);
8572 static void CreateField(int x, int y, int element)
8574 CreateFieldExt(x, y, element, FALSE);
8577 static void CreateElementFromChange(int x, int y, int element)
8579 element = GET_VALID_RUNTIME_ELEMENT(element);
8581 #if USE_STOP_CHANGED_ELEMENTS
8582 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8584 int old_element = Feld[x][y];
8586 /* prevent changed element from moving in same engine frame
8587 unless both old and new element can either fall or move */
8588 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8589 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8594 CreateFieldExt(x, y, element, TRUE);
8597 static boolean ChangeElement(int x, int y, int element, int page)
8599 struct ElementInfo *ei = &element_info[element];
8600 struct ElementChangeInfo *change = &ei->change_page[page];
8601 int ce_value = CustomValue[x][y];
8602 int ce_score = ei->collect_score;
8604 int old_element = Feld[x][y];
8606 /* always use default change event to prevent running into a loop */
8607 if (ChangeEvent[x][y] == -1)
8608 ChangeEvent[x][y] = CE_DELAY;
8610 if (ChangeEvent[x][y] == CE_DELAY)
8612 /* reset actual trigger element, trigger player and action element */
8613 change->actual_trigger_element = EL_EMPTY;
8614 change->actual_trigger_player = EL_PLAYER_1;
8615 change->actual_trigger_side = CH_SIDE_NONE;
8616 change->actual_trigger_ce_value = 0;
8617 change->actual_trigger_ce_score = 0;
8620 /* do not change elements more than a specified maximum number of changes */
8621 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8624 ChangeCount[x][y]++; /* count number of changes in the same frame */
8626 if (change->explode)
8633 if (change->use_target_content)
8635 boolean complete_replace = TRUE;
8636 boolean can_replace[3][3];
8639 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8642 boolean is_walkable;
8643 boolean is_diggable;
8644 boolean is_collectible;
8645 boolean is_removable;
8646 boolean is_destructible;
8647 int ex = x + xx - 1;
8648 int ey = y + yy - 1;
8649 int content_element = change->target_content.e[xx][yy];
8652 can_replace[xx][yy] = TRUE;
8654 if (ex == x && ey == y) /* do not check changing element itself */
8657 if (content_element == EL_EMPTY_SPACE)
8659 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8664 if (!IN_LEV_FIELD(ex, ey))
8666 can_replace[xx][yy] = FALSE;
8667 complete_replace = FALSE;
8674 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8675 e = MovingOrBlocked2Element(ex, ey);
8677 is_empty = (IS_FREE(ex, ey) ||
8678 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8680 is_walkable = (is_empty || IS_WALKABLE(e));
8681 is_diggable = (is_empty || IS_DIGGABLE(e));
8682 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8683 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8684 is_removable = (is_diggable || is_collectible);
8686 can_replace[xx][yy] =
8687 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8688 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8689 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8690 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8691 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8692 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8693 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8695 if (!can_replace[xx][yy])
8696 complete_replace = FALSE;
8699 if (!change->only_if_complete || complete_replace)
8701 boolean something_has_changed = FALSE;
8703 if (change->only_if_complete && change->use_random_replace &&
8704 RND(100) < change->random_percentage)
8707 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8709 int ex = x + xx - 1;
8710 int ey = y + yy - 1;
8711 int content_element;
8713 if (can_replace[xx][yy] && (!change->use_random_replace ||
8714 RND(100) < change->random_percentage))
8716 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8717 RemoveMovingField(ex, ey);
8719 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8721 content_element = change->target_content.e[xx][yy];
8722 target_element = GET_TARGET_ELEMENT(content_element, change,
8723 ce_value, ce_score);
8725 CreateElementFromChange(ex, ey, target_element);
8727 something_has_changed = TRUE;
8729 /* for symmetry reasons, freeze newly created border elements */
8730 if (ex != x || ey != y)
8731 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8735 if (something_has_changed)
8737 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8738 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8744 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8745 ce_value, ce_score);
8747 if (element == EL_DIAGONAL_GROWING ||
8748 element == EL_DIAGONAL_SHRINKING)
8750 target_element = Store[x][y];
8752 Store[x][y] = EL_EMPTY;
8755 CreateElementFromChange(x, y, target_element);
8757 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8758 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8761 /* this uses direct change before indirect change */
8762 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8767 #if USE_NEW_DELAYED_ACTION
8769 static void HandleElementChange(int x, int y, int page)
8771 int element = MovingOrBlocked2Element(x, y);
8772 struct ElementInfo *ei = &element_info[element];
8773 struct ElementChangeInfo *change = &ei->change_page[page];
8776 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8777 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8780 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8781 x, y, element, element_info[element].token_name);
8782 printf("HandleElementChange(): This should never happen!\n");
8787 /* this can happen with classic bombs on walkable, changing elements */
8788 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8791 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8792 ChangeDelay[x][y] = 0;
8798 if (ChangeDelay[x][y] == 0) /* initialize element change */
8800 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8802 if (change->can_change)
8804 ResetGfxAnimation(x, y);
8805 ResetRandomAnimationValue(x, y);
8807 if (change->pre_change_function)
8808 change->pre_change_function(x, y);
8812 ChangeDelay[x][y]--;
8814 if (ChangeDelay[x][y] != 0) /* continue element change */
8816 if (change->can_change)
8818 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8820 if (IS_ANIMATED(graphic))
8821 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8823 if (change->change_function)
8824 change->change_function(x, y);
8827 else /* finish element change */
8829 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8831 page = ChangePage[x][y];
8832 ChangePage[x][y] = -1;
8834 change = &ei->change_page[page];
8837 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8839 ChangeDelay[x][y] = 1; /* try change after next move step */
8840 ChangePage[x][y] = page; /* remember page to use for change */
8845 if (change->can_change)
8847 if (ChangeElement(x, y, element, page))
8849 if (change->post_change_function)
8850 change->post_change_function(x, y);
8854 if (change->has_action)
8855 ExecuteCustomElementAction(x, y, element, page);
8861 static void HandleElementChange(int x, int y, int page)
8863 int element = MovingOrBlocked2Element(x, y);
8864 struct ElementInfo *ei = &element_info[element];
8865 struct ElementChangeInfo *change = &ei->change_page[page];
8868 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8871 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8872 x, y, element, element_info[element].token_name);
8873 printf("HandleElementChange(): This should never happen!\n");
8878 /* this can happen with classic bombs on walkable, changing elements */
8879 if (!CAN_CHANGE(element))
8882 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8883 ChangeDelay[x][y] = 0;
8889 if (ChangeDelay[x][y] == 0) /* initialize element change */
8891 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8893 ResetGfxAnimation(x, y);
8894 ResetRandomAnimationValue(x, y);
8896 if (change->pre_change_function)
8897 change->pre_change_function(x, y);
8900 ChangeDelay[x][y]--;
8902 if (ChangeDelay[x][y] != 0) /* continue element change */
8904 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8906 if (IS_ANIMATED(graphic))
8907 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8909 if (change->change_function)
8910 change->change_function(x, y);
8912 else /* finish element change */
8914 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8916 page = ChangePage[x][y];
8917 ChangePage[x][y] = -1;
8919 change = &ei->change_page[page];
8922 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8924 ChangeDelay[x][y] = 1; /* try change after next move step */
8925 ChangePage[x][y] = page; /* remember page to use for change */
8930 if (ChangeElement(x, y, element, page))
8932 if (change->post_change_function)
8933 change->post_change_function(x, y);
8940 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8941 int trigger_element,
8947 boolean change_done_any = FALSE;
8948 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8951 if (!(trigger_events[trigger_element][trigger_event]))
8954 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8956 int element = EL_CUSTOM_START + i;
8957 boolean change_done = FALSE;
8960 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8961 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8964 for (p = 0; p < element_info[element].num_change_pages; p++)
8966 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8968 if (change->can_change_or_has_action &&
8969 change->has_event[trigger_event] &&
8970 change->trigger_side & trigger_side &&
8971 change->trigger_player & trigger_player &&
8972 change->trigger_page & trigger_page_bits &&
8973 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8975 change->actual_trigger_element = trigger_element;
8976 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8977 change->actual_trigger_side = trigger_side;
8978 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8979 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8981 if ((change->can_change && !change_done) || change->has_action)
8986 SCAN_PLAYFIELD(x, y)
8988 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8991 if (Feld[x][y] == element)
8993 if (change->can_change && !change_done)
8995 ChangeDelay[x][y] = 1;
8996 ChangeEvent[x][y] = trigger_event;
8998 HandleElementChange(x, y, p);
9000 #if USE_NEW_DELAYED_ACTION
9001 else if (change->has_action)
9003 ExecuteCustomElementAction(x, y, element, p);
9004 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9007 if (change->has_action)
9009 ExecuteCustomElementAction(x, y, element, p);
9010 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9016 if (change->can_change)
9019 change_done_any = TRUE;
9026 return change_done_any;
9029 static boolean CheckElementChangeExt(int x, int y,
9031 int trigger_element,
9036 boolean change_done = FALSE;
9039 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9040 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9043 if (Feld[x][y] == EL_BLOCKED)
9045 Blocked2Moving(x, y, &x, &y);
9046 element = Feld[x][y];
9050 /* check if element has already changed */
9051 if (Feld[x][y] != element)
9054 /* check if element has already changed or is about to change after moving */
9055 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9056 Feld[x][y] != element) ||
9058 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9059 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9060 ChangePage[x][y] != -1)))
9064 for (p = 0; p < element_info[element].num_change_pages; p++)
9066 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9068 boolean check_trigger_element =
9069 (trigger_event == CE_TOUCHING_X ||
9070 trigger_event == CE_HITTING_X ||
9071 trigger_event == CE_HIT_BY_X);
9073 if (change->can_change_or_has_action &&
9074 change->has_event[trigger_event] &&
9075 change->trigger_side & trigger_side &&
9076 change->trigger_player & trigger_player &&
9077 (!check_trigger_element ||
9078 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9080 change->actual_trigger_element = trigger_element;
9081 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9082 change->actual_trigger_side = trigger_side;
9083 change->actual_trigger_ce_value = CustomValue[x][y];
9084 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9086 /* special case: trigger element not at (x,y) position for some events */
9087 if (check_trigger_element)
9099 { 0, 0 }, { 0, 0 }, { 0, 0 },
9103 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9104 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9106 change->actual_trigger_ce_value = CustomValue[xx][yy];
9107 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9110 if (change->can_change && !change_done)
9112 ChangeDelay[x][y] = 1;
9113 ChangeEvent[x][y] = trigger_event;
9115 HandleElementChange(x, y, p);
9119 #if USE_NEW_DELAYED_ACTION
9120 else if (change->has_action)
9122 ExecuteCustomElementAction(x, y, element, p);
9123 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9126 if (change->has_action)
9128 ExecuteCustomElementAction(x, y, element, p);
9129 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9138 static void PlayPlayerSound(struct PlayerInfo *player)
9140 int jx = player->jx, jy = player->jy;
9141 int sound_element = player->artwork_element;
9142 int last_action = player->last_action_waiting;
9143 int action = player->action_waiting;
9145 if (player->is_waiting)
9147 if (action != last_action)
9148 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9150 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9154 if (action != last_action)
9155 StopSound(element_info[sound_element].sound[last_action]);
9157 if (last_action == ACTION_SLEEPING)
9158 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9162 static void PlayAllPlayersSound()
9166 for (i = 0; i < MAX_PLAYERS; i++)
9167 if (stored_player[i].active)
9168 PlayPlayerSound(&stored_player[i]);
9171 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9173 boolean last_waiting = player->is_waiting;
9174 int move_dir = player->MovDir;
9176 player->dir_waiting = move_dir;
9177 player->last_action_waiting = player->action_waiting;
9181 if (!last_waiting) /* not waiting -> waiting */
9183 player->is_waiting = TRUE;
9185 player->frame_counter_bored =
9187 game.player_boring_delay_fixed +
9188 SimpleRND(game.player_boring_delay_random);
9189 player->frame_counter_sleeping =
9191 game.player_sleeping_delay_fixed +
9192 SimpleRND(game.player_sleeping_delay_random);
9195 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9197 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9201 if (game.player_sleeping_delay_fixed +
9202 game.player_sleeping_delay_random > 0 &&
9203 player->anim_delay_counter == 0 &&
9204 player->post_delay_counter == 0 &&
9205 FrameCounter >= player->frame_counter_sleeping)
9206 player->is_sleeping = TRUE;
9207 else if (game.player_boring_delay_fixed +
9208 game.player_boring_delay_random > 0 &&
9209 FrameCounter >= player->frame_counter_bored)
9210 player->is_bored = TRUE;
9212 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9213 player->is_bored ? ACTION_BORING :
9217 if (player->is_sleeping && player->use_murphy)
9219 /* special case for sleeping Murphy when leaning against non-free tile */
9221 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9222 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9223 !IS_MOVING(player->jx - 1, player->jy)))
9225 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9226 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9227 !IS_MOVING(player->jx + 1, player->jy)))
9228 move_dir = MV_RIGHT;
9230 player->is_sleeping = FALSE;
9232 player->dir_waiting = move_dir;
9236 if (player->is_sleeping)
9238 if (player->num_special_action_sleeping > 0)
9240 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9242 int last_special_action = player->special_action_sleeping;
9243 int num_special_action = player->num_special_action_sleeping;
9244 int special_action =
9245 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9246 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9247 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9248 last_special_action + 1 : ACTION_SLEEPING);
9249 int special_graphic =
9250 el_act_dir2img(player->artwork_element, special_action, move_dir);
9252 player->anim_delay_counter =
9253 graphic_info[special_graphic].anim_delay_fixed +
9254 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9255 player->post_delay_counter =
9256 graphic_info[special_graphic].post_delay_fixed +
9257 SimpleRND(graphic_info[special_graphic].post_delay_random);
9259 player->special_action_sleeping = special_action;
9262 if (player->anim_delay_counter > 0)
9264 player->action_waiting = player->special_action_sleeping;
9265 player->anim_delay_counter--;
9267 else if (player->post_delay_counter > 0)
9269 player->post_delay_counter--;
9273 else if (player->is_bored)
9275 if (player->num_special_action_bored > 0)
9277 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9279 int special_action =
9280 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9281 int special_graphic =
9282 el_act_dir2img(player->artwork_element, special_action, move_dir);
9284 player->anim_delay_counter =
9285 graphic_info[special_graphic].anim_delay_fixed +
9286 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9287 player->post_delay_counter =
9288 graphic_info[special_graphic].post_delay_fixed +
9289 SimpleRND(graphic_info[special_graphic].post_delay_random);
9291 player->special_action_bored = special_action;
9294 if (player->anim_delay_counter > 0)
9296 player->action_waiting = player->special_action_bored;
9297 player->anim_delay_counter--;
9299 else if (player->post_delay_counter > 0)
9301 player->post_delay_counter--;
9306 else if (last_waiting) /* waiting -> not waiting */
9308 player->is_waiting = FALSE;
9309 player->is_bored = FALSE;
9310 player->is_sleeping = FALSE;
9312 player->frame_counter_bored = -1;
9313 player->frame_counter_sleeping = -1;
9315 player->anim_delay_counter = 0;
9316 player->post_delay_counter = 0;
9318 player->dir_waiting = player->MovDir;
9319 player->action_waiting = ACTION_DEFAULT;
9321 player->special_action_bored = ACTION_DEFAULT;
9322 player->special_action_sleeping = ACTION_DEFAULT;
9326 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9328 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9329 int left = player_action & JOY_LEFT;
9330 int right = player_action & JOY_RIGHT;
9331 int up = player_action & JOY_UP;
9332 int down = player_action & JOY_DOWN;
9333 int button1 = player_action & JOY_BUTTON_1;
9334 int button2 = player_action & JOY_BUTTON_2;
9335 int dx = (left ? -1 : right ? 1 : 0);
9336 int dy = (up ? -1 : down ? 1 : 0);
9338 if (!player->active || tape.pausing)
9344 snapped = SnapField(player, dx, dy);
9348 dropped = DropElement(player);
9350 moved = MovePlayer(player, dx, dy);
9353 if (tape.single_step && tape.recording && !tape.pausing)
9355 if (button1 || (dropped && !moved))
9357 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9358 SnapField(player, 0, 0); /* stop snapping */
9362 SetPlayerWaiting(player, FALSE);
9364 return player_action;
9368 /* no actions for this player (no input at player's configured device) */
9370 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9371 SnapField(player, 0, 0);
9372 CheckGravityMovementWhenNotMoving(player);
9374 if (player->MovPos == 0)
9375 SetPlayerWaiting(player, TRUE);
9377 if (player->MovPos == 0) /* needed for tape.playing */
9378 player->is_moving = FALSE;
9380 player->is_dropping = FALSE;
9381 player->is_dropping_pressed = FALSE;
9382 player->drop_pressed_delay = 0;
9388 static void CheckLevelTime()
9392 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9394 if (level.native_em_level->lev->home == 0) /* all players at home */
9396 local_player->LevelSolved = TRUE;
9397 AllPlayersGone = TRUE;
9399 level.native_em_level->lev->home = -1;
9402 if (level.native_em_level->ply[0]->alive == 0 &&
9403 level.native_em_level->ply[1]->alive == 0 &&
9404 level.native_em_level->ply[2]->alive == 0 &&
9405 level.native_em_level->ply[3]->alive == 0) /* all dead */
9406 AllPlayersGone = TRUE;
9409 if (TimeFrames >= FRAMES_PER_SECOND)
9414 for (i = 0; i < MAX_PLAYERS; i++)
9416 struct PlayerInfo *player = &stored_player[i];
9418 if (SHIELD_ON(player))
9420 player->shield_normal_time_left--;
9422 if (player->shield_deadly_time_left > 0)
9423 player->shield_deadly_time_left--;
9427 if (!level.use_step_counter)
9435 if (TimeLeft <= 10 && setup.time_limit)
9436 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9438 DrawGameValue_Time(TimeLeft);
9440 if (!TimeLeft && setup.time_limit)
9442 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9443 level.native_em_level->lev->killed_out_of_time = TRUE;
9445 for (i = 0; i < MAX_PLAYERS; i++)
9446 KillPlayer(&stored_player[i]);
9449 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9450 DrawGameValue_Time(TimePlayed);
9452 level.native_em_level->lev->time =
9453 (level.time == 0 ? TimePlayed : TimeLeft);
9456 if (tape.recording || tape.playing)
9457 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9461 void AdvanceFrameAndPlayerCounters(int player_nr)
9466 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9467 FrameCounter, FrameCounter + 1);
9470 /* advance frame counters (global frame counter and time frame counter) */
9474 /* advance player counters (counters for move delay, move animation etc.) */
9475 for (i = 0; i < MAX_PLAYERS; i++)
9477 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9478 int move_delay_value = stored_player[i].move_delay_value;
9479 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9481 if (!advance_player_counters) /* not all players may be affected */
9484 #if USE_NEW_PLAYER_ANIM
9485 if (move_frames == 0) /* less than one move per game frame */
9487 int stepsize = TILEX / move_delay_value;
9488 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9489 int count = (stored_player[i].is_moving ?
9490 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9492 if (count % delay == 0)
9497 stored_player[i].Frame += move_frames;
9499 if (stored_player[i].MovPos != 0)
9500 stored_player[i].StepFrame += move_frames;
9502 if (stored_player[i].move_delay > 0)
9503 stored_player[i].move_delay--;
9505 /* due to bugs in previous versions, counter must count up, not down */
9506 if (stored_player[i].push_delay != -1)
9507 stored_player[i].push_delay++;
9509 if (stored_player[i].drop_delay > 0)
9510 stored_player[i].drop_delay--;
9512 if (stored_player[i].is_dropping_pressed)
9513 stored_player[i].drop_pressed_delay++;
9517 void StartGameActions(boolean init_network_game, boolean record_tape,
9520 unsigned long new_random_seed = InitRND(random_seed);
9523 TapeStartRecording(new_random_seed);
9525 #if defined(NETWORK_AVALIABLE)
9526 if (init_network_game)
9528 SendToServer_StartPlaying();
9536 game_status = GAME_MODE_PLAYING;
9543 static unsigned long game_frame_delay = 0;
9544 unsigned long game_frame_delay_value;
9545 byte *recorded_player_action;
9546 byte summarized_player_action = 0;
9547 byte tape_action[MAX_PLAYERS];
9550 if (game.restart_level)
9551 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9553 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9555 if (level.native_em_level->lev->home == 0) /* all players at home */
9557 local_player->LevelSolved = TRUE;
9558 AllPlayersGone = TRUE;
9560 level.native_em_level->lev->home = -1;
9563 if (level.native_em_level->ply[0]->alive == 0 &&
9564 level.native_em_level->ply[1]->alive == 0 &&
9565 level.native_em_level->ply[2]->alive == 0 &&
9566 level.native_em_level->ply[3]->alive == 0) /* all dead */
9567 AllPlayersGone = TRUE;
9570 if (local_player->LevelSolved)
9573 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9576 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9579 game_frame_delay_value =
9580 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9582 if (tape.playing && tape.warp_forward && !tape.pausing)
9583 game_frame_delay_value = 0;
9585 /* ---------- main game synchronization point ---------- */
9587 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9589 if (network_playing && !network_player_action_received)
9591 /* try to get network player actions in time */
9593 #if defined(NETWORK_AVALIABLE)
9594 /* last chance to get network player actions without main loop delay */
9598 /* game was quit by network peer */
9599 if (game_status != GAME_MODE_PLAYING)
9602 if (!network_player_action_received)
9603 return; /* failed to get network player actions in time */
9605 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9611 /* at this point we know that we really continue executing the game */
9614 network_player_action_received = FALSE;
9617 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9619 if (tape.set_centered_player)
9621 game.centered_player_nr_next = tape.centered_player_nr_next;
9622 game.set_centered_player = TRUE;
9625 for (i = 0; i < MAX_PLAYERS; i++)
9627 summarized_player_action |= stored_player[i].action;
9629 if (!network_playing)
9630 stored_player[i].effective_action = stored_player[i].action;
9633 #if defined(NETWORK_AVALIABLE)
9634 if (network_playing)
9635 SendToServer_MovePlayer(summarized_player_action);
9638 if (!options.network && !setup.team_mode)
9639 local_player->effective_action = summarized_player_action;
9641 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9643 for (i = 0; i < MAX_PLAYERS; i++)
9644 stored_player[i].effective_action =
9645 (i == game.centered_player_nr ? summarized_player_action : 0);
9648 if (recorded_player_action != NULL)
9649 for (i = 0; i < MAX_PLAYERS; i++)
9650 stored_player[i].effective_action = recorded_player_action[i];
9652 for (i = 0; i < MAX_PLAYERS; i++)
9654 tape_action[i] = stored_player[i].effective_action;
9656 /* (this can only happen in the R'n'D game engine) */
9657 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9658 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9661 /* only record actions from input devices, but not programmed actions */
9663 TapeRecordAction(tape_action);
9665 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9667 GameActions_EM_Main();
9675 void GameActions_EM_Main()
9677 byte effective_action[MAX_PLAYERS];
9678 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9681 for (i = 0; i < MAX_PLAYERS; i++)
9682 effective_action[i] = stored_player[i].effective_action;
9685 printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
9688 GameActions_EM(effective_action, warp_mode);
9692 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9695 void GameActions_RND()
9697 int magic_wall_x = 0, magic_wall_y = 0;
9698 int i, x, y, element, graphic;
9700 InitPlayfieldScanModeVars();
9702 #if USE_ONE_MORE_CHANGE_PER_FRAME
9703 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9705 SCAN_PLAYFIELD(x, y)
9707 ChangeCount[x][y] = 0;
9708 ChangeEvent[x][y] = -1;
9714 if (game.set_centered_player)
9716 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9718 /* switching to "all players" only possible if all players fit to screen */
9719 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9721 game.centered_player_nr_next = game.centered_player_nr;
9722 game.set_centered_player = FALSE;
9725 /* do not switch focus to non-existing (or non-active) player */
9726 if (game.centered_player_nr_next >= 0 &&
9727 !stored_player[game.centered_player_nr_next].active)
9729 game.centered_player_nr_next = game.centered_player_nr;
9730 game.set_centered_player = FALSE;
9734 if (game.set_centered_player &&
9735 ScreenMovPos == 0) /* screen currently aligned at tile position */
9739 if (game.centered_player_nr_next == -1)
9741 setScreenCenteredToAllPlayers(&sx, &sy);
9745 sx = stored_player[game.centered_player_nr_next].jx;
9746 sy = stored_player[game.centered_player_nr_next].jy;
9749 game.centered_player_nr = game.centered_player_nr_next;
9750 game.set_centered_player = FALSE;
9752 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9753 DrawGameDoorValues();
9757 for (i = 0; i < MAX_PLAYERS; i++)
9759 int actual_player_action = stored_player[i].effective_action;
9762 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9763 - rnd_equinox_tetrachloride 048
9764 - rnd_equinox_tetrachloride_ii 096
9765 - rnd_emanuel_schmieg 002
9766 - doctor_sloan_ww 001, 020
9768 if (stored_player[i].MovPos == 0)
9769 CheckGravityMovement(&stored_player[i]);
9772 /* overwrite programmed action with tape action */
9773 if (stored_player[i].programmed_action)
9774 actual_player_action = stored_player[i].programmed_action;
9777 PlayerActions(&stored_player[i], actual_player_action);
9779 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9781 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9782 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9785 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9789 network_player_action_received = FALSE;
9792 ScrollScreen(NULL, SCROLL_GO_ON);
9794 /* for backwards compatibility, the following code emulates a fixed bug that
9795 occured when pushing elements (causing elements that just made their last
9796 pushing step to already (if possible) make their first falling step in the
9797 same game frame, which is bad); this code is also needed to use the famous
9798 "spring push bug" which is used in older levels and might be wanted to be
9799 used also in newer levels, but in this case the buggy pushing code is only
9800 affecting the "spring" element and no other elements */
9802 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9804 for (i = 0; i < MAX_PLAYERS; i++)
9806 struct PlayerInfo *player = &stored_player[i];
9810 if (player->active && player->is_pushing && player->is_moving &&
9812 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9813 Feld[x][y] == EL_SPRING))
9815 ContinueMoving(x, y);
9817 /* continue moving after pushing (this is actually a bug) */
9818 if (!IS_MOVING(x, y))
9827 SCAN_PLAYFIELD(x, y)
9829 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9832 ChangeCount[x][y] = 0;
9833 ChangeEvent[x][y] = -1;
9835 /* this must be handled before main playfield loop */
9836 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9839 if (MovDelay[x][y] <= 0)
9843 #if USE_NEW_SNAP_DELAY
9844 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9847 if (MovDelay[x][y] <= 0)
9850 DrawLevelField(x, y);
9852 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9858 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9860 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9861 printf("GameActions(): This should never happen!\n");
9863 ChangePage[x][y] = -1;
9868 if (WasJustMoving[x][y] > 0)
9869 WasJustMoving[x][y]--;
9870 if (WasJustFalling[x][y] > 0)
9871 WasJustFalling[x][y]--;
9872 if (CheckCollision[x][y] > 0)
9873 CheckCollision[x][y]--;
9877 /* reset finished pushing action (not done in ContinueMoving() to allow
9878 continuous pushing animation for elements with zero push delay) */
9879 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9881 ResetGfxAnimation(x, y);
9882 DrawLevelField(x, y);
9886 if (IS_BLOCKED(x, y))
9890 Blocked2Moving(x, y, &oldx, &oldy);
9891 if (!IS_MOVING(oldx, oldy))
9893 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9894 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9895 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9896 printf("GameActions(): This should never happen!\n");
9903 SCAN_PLAYFIELD(x, y)
9905 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9908 element = Feld[x][y];
9909 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9912 printf("::: %d,%d\n", x, y);
9914 if (element == EL_ROCK)
9915 printf("::: Yo man! Rocks can fall!\n");
9919 ResetGfxFrame(x, y, TRUE);
9921 if (graphic_info[graphic].anim_global_sync)
9922 GfxFrame[x][y] = FrameCounter;
9923 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9925 int old_gfx_frame = GfxFrame[x][y];
9927 GfxFrame[x][y] = CustomValue[x][y];
9930 if (GfxFrame[x][y] != old_gfx_frame)
9932 DrawLevelGraphicAnimation(x, y, graphic);
9934 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9936 int old_gfx_frame = GfxFrame[x][y];
9938 GfxFrame[x][y] = element_info[element].collect_score;
9941 if (GfxFrame[x][y] != old_gfx_frame)
9943 DrawLevelGraphicAnimation(x, y, graphic);
9945 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9947 int old_gfx_frame = GfxFrame[x][y];
9949 GfxFrame[x][y] = ChangeDelay[x][y];
9952 if (GfxFrame[x][y] != old_gfx_frame)
9954 DrawLevelGraphicAnimation(x, y, graphic);
9958 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9959 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9960 ResetRandomAnimationValue(x, y);
9962 SetRandomAnimationValue(x, y);
9964 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9966 if (IS_INACTIVE(element))
9968 if (IS_ANIMATED(graphic))
9969 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9974 /* this may take place after moving, so 'element' may have changed */
9975 if (IS_CHANGING(x, y) &&
9976 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9978 int page = element_info[element].event_page_nr[CE_DELAY];
9980 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9984 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9988 if (element == EL_CUSTOM_255)
9989 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9993 HandleElementChange(x, y, page);
9995 if (CAN_CHANGE(element))
9996 HandleElementChange(x, y, page);
9998 if (HAS_ACTION(element))
9999 ExecuteCustomElementAction(x, y, element, page);
10004 element = Feld[x][y];
10005 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10008 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10012 element = Feld[x][y];
10013 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10015 if (IS_ANIMATED(graphic) &&
10016 !IS_MOVING(x, y) &&
10018 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10020 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10021 EdelsteinFunkeln(x, y);
10023 else if ((element == EL_ACID ||
10024 element == EL_EXIT_OPEN ||
10025 element == EL_SP_EXIT_OPEN ||
10026 element == EL_SP_TERMINAL ||
10027 element == EL_SP_TERMINAL_ACTIVE ||
10028 element == EL_EXTRA_TIME ||
10029 element == EL_SHIELD_NORMAL ||
10030 element == EL_SHIELD_DEADLY) &&
10031 IS_ANIMATED(graphic))
10032 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10033 else if (IS_MOVING(x, y))
10034 ContinueMoving(x, y);
10035 else if (IS_ACTIVE_BOMB(element))
10036 CheckDynamite(x, y);
10037 else if (element == EL_AMOEBA_GROWING)
10038 AmoebeWaechst(x, y);
10039 else if (element == EL_AMOEBA_SHRINKING)
10040 AmoebaDisappearing(x, y);
10042 #if !USE_NEW_AMOEBA_CODE
10043 else if (IS_AMOEBALIVE(element))
10044 AmoebeAbleger(x, y);
10047 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10049 else if (element == EL_EXIT_CLOSED)
10051 else if (element == EL_SP_EXIT_CLOSED)
10053 else if (element == EL_EXPANDABLE_WALL_GROWING)
10054 MauerWaechst(x, y);
10055 else if (element == EL_EXPANDABLE_WALL ||
10056 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10057 element == EL_EXPANDABLE_WALL_VERTICAL ||
10058 element == EL_EXPANDABLE_WALL_ANY ||
10059 element == EL_BD_EXPANDABLE_WALL)
10060 MauerAbleger(x, y);
10061 else if (element == EL_FLAMES)
10062 CheckForDragon(x, y);
10063 else if (element == EL_EXPLOSION)
10064 ; /* drawing of correct explosion animation is handled separately */
10065 else if (element == EL_ELEMENT_SNAPPING ||
10066 element == EL_DIAGONAL_SHRINKING ||
10067 element == EL_DIAGONAL_GROWING)
10070 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10072 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10075 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10076 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10079 if (element == EL_CUSTOM_255 ||
10080 element == EL_CUSTOM_256)
10081 DrawLevelGraphicAnimation(x, y, graphic);
10084 if (IS_BELT_ACTIVE(element))
10085 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10087 if (game.magic_wall_active)
10089 int jx = local_player->jx, jy = local_player->jy;
10091 /* play the element sound at the position nearest to the player */
10092 if ((element == EL_MAGIC_WALL_FULL ||
10093 element == EL_MAGIC_WALL_ACTIVE ||
10094 element == EL_MAGIC_WALL_EMPTYING ||
10095 element == EL_BD_MAGIC_WALL_FULL ||
10096 element == EL_BD_MAGIC_WALL_ACTIVE ||
10097 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10098 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10106 #if USE_NEW_AMOEBA_CODE
10107 /* new experimental amoeba growth stuff */
10108 if (!(FrameCounter % 8))
10110 static unsigned long random = 1684108901;
10112 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10114 x = RND(lev_fieldx);
10115 y = RND(lev_fieldy);
10116 element = Feld[x][y];
10118 if (!IS_PLAYER(x,y) &&
10119 (element == EL_EMPTY ||
10120 CAN_GROW_INTO(element) ||
10121 element == EL_QUICKSAND_EMPTY ||
10122 element == EL_ACID_SPLASH_LEFT ||
10123 element == EL_ACID_SPLASH_RIGHT))
10125 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10126 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10127 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10128 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10129 Feld[x][y] = EL_AMOEBA_DROP;
10132 random = random * 129 + 1;
10138 if (game.explosions_delayed)
10141 game.explosions_delayed = FALSE;
10144 SCAN_PLAYFIELD(x, y)
10146 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10149 element = Feld[x][y];
10151 if (ExplodeField[x][y])
10152 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10153 else if (element == EL_EXPLOSION)
10154 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10156 ExplodeField[x][y] = EX_TYPE_NONE;
10159 game.explosions_delayed = TRUE;
10162 if (game.magic_wall_active)
10164 if (!(game.magic_wall_time_left % 4))
10166 int element = Feld[magic_wall_x][magic_wall_y];
10168 if (element == EL_BD_MAGIC_WALL_FULL ||
10169 element == EL_BD_MAGIC_WALL_ACTIVE ||
10170 element == EL_BD_MAGIC_WALL_EMPTYING)
10171 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10173 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10176 if (game.magic_wall_time_left > 0)
10178 game.magic_wall_time_left--;
10179 if (!game.magic_wall_time_left)
10182 SCAN_PLAYFIELD(x, y)
10184 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10187 element = Feld[x][y];
10189 if (element == EL_MAGIC_WALL_ACTIVE ||
10190 element == EL_MAGIC_WALL_FULL)
10192 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10193 DrawLevelField(x, y);
10195 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10196 element == EL_BD_MAGIC_WALL_FULL)
10198 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10199 DrawLevelField(x, y);
10203 game.magic_wall_active = FALSE;
10208 if (game.light_time_left > 0)
10210 game.light_time_left--;
10212 if (game.light_time_left == 0)
10213 RedrawAllLightSwitchesAndInvisibleElements();
10216 if (game.timegate_time_left > 0)
10218 game.timegate_time_left--;
10220 if (game.timegate_time_left == 0)
10221 CloseAllOpenTimegates();
10224 if (game.lenses_time_left > 0)
10226 game.lenses_time_left--;
10228 if (game.lenses_time_left == 0)
10229 RedrawAllInvisibleElementsForLenses();
10232 if (game.magnify_time_left > 0)
10234 game.magnify_time_left--;
10236 if (game.magnify_time_left == 0)
10237 RedrawAllInvisibleElementsForMagnifier();
10240 for (i = 0; i < MAX_PLAYERS; i++)
10242 struct PlayerInfo *player = &stored_player[i];
10244 if (SHIELD_ON(player))
10246 if (player->shield_deadly_time_left)
10247 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10248 else if (player->shield_normal_time_left)
10249 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10256 PlayAllPlayersSound();
10258 if (options.debug) /* calculate frames per second */
10260 static unsigned long fps_counter = 0;
10261 static int fps_frames = 0;
10262 unsigned long fps_delay_ms = Counter() - fps_counter;
10266 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10268 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10271 fps_counter = Counter();
10274 redraw_mask |= REDRAW_FPS;
10277 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10279 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10281 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10283 local_player->show_envelope = 0;
10286 /* use random number generator in every frame to make it less predictable */
10287 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10291 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10293 int min_x = x, min_y = y, max_x = x, max_y = y;
10296 for (i = 0; i < MAX_PLAYERS; i++)
10298 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10300 if (!stored_player[i].active || &stored_player[i] == player)
10303 min_x = MIN(min_x, jx);
10304 min_y = MIN(min_y, jy);
10305 max_x = MAX(max_x, jx);
10306 max_y = MAX(max_y, jy);
10309 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10312 static boolean AllPlayersInVisibleScreen()
10316 for (i = 0; i < MAX_PLAYERS; i++)
10318 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10320 if (!stored_player[i].active)
10323 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10330 void ScrollLevel(int dx, int dy)
10332 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10335 BlitBitmap(drawto_field, drawto_field,
10336 FX + TILEX * (dx == -1) - softscroll_offset,
10337 FY + TILEY * (dy == -1) - softscroll_offset,
10338 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10339 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10340 FX + TILEX * (dx == 1) - softscroll_offset,
10341 FY + TILEY * (dy == 1) - softscroll_offset);
10345 x = (dx == 1 ? BX1 : BX2);
10346 for (y = BY1; y <= BY2; y++)
10347 DrawScreenField(x, y);
10352 y = (dy == 1 ? BY1 : BY2);
10353 for (x = BX1; x <= BX2; x++)
10354 DrawScreenField(x, y);
10357 redraw_mask |= REDRAW_FIELD;
10360 static boolean canFallDown(struct PlayerInfo *player)
10362 int jx = player->jx, jy = player->jy;
10364 return (IN_LEV_FIELD(jx, jy + 1) &&
10365 (IS_FREE(jx, jy + 1) ||
10366 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10367 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10368 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10371 static boolean canPassField(int x, int y, int move_dir)
10373 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10374 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10375 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10376 int nextx = x + dx;
10377 int nexty = y + dy;
10378 int element = Feld[x][y];
10380 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10381 !CAN_MOVE(element) &&
10382 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10383 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10384 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10387 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10389 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10390 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10391 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10395 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10396 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10397 (IS_DIGGABLE(Feld[newx][newy]) ||
10398 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10399 canPassField(newx, newy, move_dir)));
10402 static void CheckGravityMovement(struct PlayerInfo *player)
10404 #if USE_PLAYER_GRAVITY
10405 if (player->gravity && !player->programmed_action)
10407 if (game.gravity && !player->programmed_action)
10410 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10411 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10412 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10413 int jx = player->jx, jy = player->jy;
10414 boolean player_is_moving_to_valid_field =
10415 (!player_is_snapping &&
10416 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10417 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10418 boolean player_can_fall_down = canFallDown(player);
10420 if (player_can_fall_down &&
10421 !player_is_moving_to_valid_field)
10422 player->programmed_action = MV_DOWN;
10426 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10428 return CheckGravityMovement(player);
10430 #if USE_PLAYER_GRAVITY
10431 if (player->gravity && !player->programmed_action)
10433 if (game.gravity && !player->programmed_action)
10436 int jx = player->jx, jy = player->jy;
10437 boolean field_under_player_is_free =
10438 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10439 boolean player_is_standing_on_valid_field =
10440 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10441 (IS_WALKABLE(Feld[jx][jy]) &&
10442 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10444 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10445 player->programmed_action = MV_DOWN;
10450 MovePlayerOneStep()
10451 -----------------------------------------------------------------------------
10452 dx, dy: direction (non-diagonal) to try to move the player to
10453 real_dx, real_dy: direction as read from input device (can be diagonal)
10456 boolean MovePlayerOneStep(struct PlayerInfo *player,
10457 int dx, int dy, int real_dx, int real_dy)
10459 int jx = player->jx, jy = player->jy;
10460 int new_jx = jx + dx, new_jy = jy + dy;
10461 #if !USE_FIXED_DONT_RUN_INTO
10465 boolean player_can_move = !player->cannot_move;
10467 if (!player->active || (!dx && !dy))
10468 return MP_NO_ACTION;
10470 player->MovDir = (dx < 0 ? MV_LEFT :
10471 dx > 0 ? MV_RIGHT :
10473 dy > 0 ? MV_DOWN : MV_NONE);
10475 if (!IN_LEV_FIELD(new_jx, new_jy))
10476 return MP_NO_ACTION;
10478 if (!player_can_move)
10481 if (player->MovPos == 0)
10483 player->is_moving = FALSE;
10484 player->is_digging = FALSE;
10485 player->is_collecting = FALSE;
10486 player->is_snapping = FALSE;
10487 player->is_pushing = FALSE;
10490 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10491 SnapField(player, 0, 0);
10495 return MP_NO_ACTION;
10500 if (!options.network && game.centered_player_nr == -1 &&
10501 !AllPlayersInSight(player, new_jx, new_jy))
10502 return MP_NO_ACTION;
10504 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10505 return MP_NO_ACTION;
10508 #if !USE_FIXED_DONT_RUN_INTO
10509 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10511 /* (moved to DigField()) */
10512 if (player_can_move && DONT_RUN_INTO(element))
10514 if (element == EL_ACID && dx == 0 && dy == 1)
10516 SplashAcid(new_jx, new_jy);
10517 Feld[jx][jy] = EL_PLAYER_1;
10518 InitMovingField(jx, jy, MV_DOWN);
10519 Store[jx][jy] = EL_ACID;
10520 ContinueMoving(jx, jy);
10521 BuryPlayer(player);
10524 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10530 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10532 #if USE_FIXED_DONT_RUN_INTO
10533 if (can_move == MP_DONT_RUN_INTO)
10537 if (can_move != MP_MOVING)
10540 #if USE_FIXED_DONT_RUN_INTO
10543 /* check if DigField() has caused relocation of the player */
10544 if (player->jx != jx || player->jy != jy)
10545 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10547 StorePlayer[jx][jy] = 0;
10548 player->last_jx = jx;
10549 player->last_jy = jy;
10550 player->jx = new_jx;
10551 player->jy = new_jy;
10552 StorePlayer[new_jx][new_jy] = player->element_nr;
10554 if (player->move_delay_value_next != -1)
10556 player->move_delay_value = player->move_delay_value_next;
10557 player->move_delay_value_next = -1;
10561 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10563 player->step_counter++;
10565 PlayerVisit[jx][jy] = FrameCounter;
10567 ScrollPlayer(player, SCROLL_INIT);
10572 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10574 int jx = player->jx, jy = player->jy;
10575 int old_jx = jx, old_jy = jy;
10576 int moved = MP_NO_ACTION;
10578 if (!player->active)
10583 if (player->MovPos == 0)
10585 player->is_moving = FALSE;
10586 player->is_digging = FALSE;
10587 player->is_collecting = FALSE;
10588 player->is_snapping = FALSE;
10589 player->is_pushing = FALSE;
10595 if (player->move_delay > 0)
10598 player->move_delay = -1; /* set to "uninitialized" value */
10600 /* store if player is automatically moved to next field */
10601 player->is_auto_moving = (player->programmed_action != MV_NONE);
10603 /* remove the last programmed player action */
10604 player->programmed_action = 0;
10606 if (player->MovPos)
10608 /* should only happen if pre-1.2 tape recordings are played */
10609 /* this is only for backward compatibility */
10611 int original_move_delay_value = player->move_delay_value;
10614 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10618 /* scroll remaining steps with finest movement resolution */
10619 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10621 while (player->MovPos)
10623 ScrollPlayer(player, SCROLL_GO_ON);
10624 ScrollScreen(NULL, SCROLL_GO_ON);
10626 AdvanceFrameAndPlayerCounters(player->index_nr);
10632 player->move_delay_value = original_move_delay_value;
10635 player->is_active = FALSE;
10637 if (player->last_move_dir & MV_HORIZONTAL)
10639 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10640 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10644 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10645 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10648 #if USE_FIXED_BORDER_RUNNING_GFX
10649 if (!moved && !player->is_active)
10651 player->is_moving = FALSE;
10652 player->is_digging = FALSE;
10653 player->is_collecting = FALSE;
10654 player->is_snapping = FALSE;
10655 player->is_pushing = FALSE;
10663 if (moved & MP_MOVING && !ScreenMovPos &&
10664 (player->index_nr == game.centered_player_nr ||
10665 game.centered_player_nr == -1))
10667 if (moved & MP_MOVING && !ScreenMovPos &&
10668 (player == local_player || !options.network))
10671 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10672 int offset = (setup.scroll_delay ? 3 : 0);
10674 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10676 /* actual player has left the screen -- scroll in that direction */
10677 if (jx != old_jx) /* player has moved horizontally */
10678 scroll_x += (jx - old_jx);
10679 else /* player has moved vertically */
10680 scroll_y += (jy - old_jy);
10684 if (jx != old_jx) /* player has moved horizontally */
10686 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10687 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10688 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10690 /* don't scroll over playfield boundaries */
10691 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10692 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10694 /* don't scroll more than one field at a time */
10695 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10697 /* don't scroll against the player's moving direction */
10698 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10699 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10700 scroll_x = old_scroll_x;
10702 else /* player has moved vertically */
10704 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10705 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10706 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10708 /* don't scroll over playfield boundaries */
10709 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10710 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10712 /* don't scroll more than one field at a time */
10713 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10715 /* don't scroll against the player's moving direction */
10716 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10717 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10718 scroll_y = old_scroll_y;
10722 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10725 if (!options.network && game.centered_player_nr == -1 &&
10726 !AllPlayersInVisibleScreen())
10728 scroll_x = old_scroll_x;
10729 scroll_y = old_scroll_y;
10733 if (!options.network && !AllPlayersInVisibleScreen())
10735 scroll_x = old_scroll_x;
10736 scroll_y = old_scroll_y;
10741 ScrollScreen(player, SCROLL_INIT);
10742 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10747 player->StepFrame = 0;
10749 if (moved & MP_MOVING)
10751 if (old_jx != jx && old_jy == jy)
10752 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10753 else if (old_jx == jx && old_jy != jy)
10754 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10756 DrawLevelField(jx, jy); /* for "crumbled sand" */
10758 player->last_move_dir = player->MovDir;
10759 player->is_moving = TRUE;
10760 player->is_snapping = FALSE;
10761 player->is_switching = FALSE;
10762 player->is_dropping = FALSE;
10763 player->is_dropping_pressed = FALSE;
10764 player->drop_pressed_delay = 0;
10768 CheckGravityMovementWhenNotMoving(player);
10770 player->is_moving = FALSE;
10772 /* at this point, the player is allowed to move, but cannot move right now
10773 (e.g. because of something blocking the way) -- ensure that the player
10774 is also allowed to move in the next frame (in old versions before 3.1.1,
10775 the player was forced to wait again for eight frames before next try) */
10777 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10778 player->move_delay = 0; /* allow direct movement in the next frame */
10781 if (player->move_delay == -1) /* not yet initialized by DigField() */
10782 player->move_delay = player->move_delay_value;
10784 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10786 TestIfPlayerTouchesBadThing(jx, jy);
10787 TestIfPlayerTouchesCustomElement(jx, jy);
10790 if (!player->active)
10791 RemovePlayer(player);
10796 void ScrollPlayer(struct PlayerInfo *player, int mode)
10798 int jx = player->jx, jy = player->jy;
10799 int last_jx = player->last_jx, last_jy = player->last_jy;
10800 int move_stepsize = TILEX / player->move_delay_value;
10802 #if USE_NEW_PLAYER_SPEED
10803 if (!player->active)
10806 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10809 if (!player->active || player->MovPos == 0)
10813 if (mode == SCROLL_INIT)
10815 player->actual_frame_counter = FrameCounter;
10816 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10818 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10819 Feld[last_jx][last_jy] == EL_EMPTY)
10821 int last_field_block_delay = 0; /* start with no blocking at all */
10822 int block_delay_adjustment = player->block_delay_adjustment;
10824 /* if player blocks last field, add delay for exactly one move */
10825 if (player->block_last_field)
10827 last_field_block_delay += player->move_delay_value;
10829 /* when blocking enabled, prevent moving up despite gravity */
10830 #if USE_PLAYER_GRAVITY
10831 if (player->gravity && player->MovDir == MV_UP)
10832 block_delay_adjustment = -1;
10834 if (game.gravity && player->MovDir == MV_UP)
10835 block_delay_adjustment = -1;
10839 /* add block delay adjustment (also possible when not blocking) */
10840 last_field_block_delay += block_delay_adjustment;
10842 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10843 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10846 #if USE_NEW_PLAYER_SPEED
10847 if (player->MovPos != 0) /* player has not yet reached destination */
10853 else if (!FrameReached(&player->actual_frame_counter, 1))
10857 printf("::: player->MovPos: %d -> %d\n",
10859 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10862 #if USE_NEW_PLAYER_SPEED
10863 if (player->MovPos != 0)
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 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10874 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10876 /* before DrawPlayer() to draw correct player graphic for this case */
10877 if (player->MovPos == 0)
10878 CheckGravityMovement(player);
10881 if (player->MovPos == 0) /* player reached destination field */
10884 printf("::: player reached destination field\n");
10887 if (player->move_delay_reset_counter > 0)
10889 player->move_delay_reset_counter--;
10891 if (player->move_delay_reset_counter == 0)
10893 /* continue with normal speed after quickly moving through gate */
10894 HALVE_PLAYER_SPEED(player);
10896 /* be able to make the next move without delay */
10897 player->move_delay = 0;
10901 player->last_jx = jx;
10902 player->last_jy = jy;
10904 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10905 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10906 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10908 DrawPlayer(player); /* needed here only to cleanup last field */
10909 RemovePlayer(player);
10911 if (local_player->friends_still_needed == 0 ||
10912 IS_SP_ELEMENT(Feld[jx][jy]))
10913 player->LevelSolved = player->GameOver = TRUE;
10916 /* this breaks one level: "machine", level 000 */
10918 int move_direction = player->MovDir;
10919 int enter_side = MV_DIR_OPPOSITE(move_direction);
10920 int leave_side = move_direction;
10921 int old_jx = last_jx;
10922 int old_jy = last_jy;
10923 int old_element = Feld[old_jx][old_jy];
10924 int new_element = Feld[jx][jy];
10926 if (IS_CUSTOM_ELEMENT(old_element))
10927 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10929 player->index_bit, leave_side);
10931 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10932 CE_PLAYER_LEAVES_X,
10933 player->index_bit, leave_side);
10935 if (IS_CUSTOM_ELEMENT(new_element))
10936 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10937 player->index_bit, enter_side);
10939 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10940 CE_PLAYER_ENTERS_X,
10941 player->index_bit, enter_side);
10943 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10944 CE_MOVE_OF_X, move_direction);
10947 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10949 TestIfPlayerTouchesBadThing(jx, jy);
10950 TestIfPlayerTouchesCustomElement(jx, jy);
10952 /* needed because pushed element has not yet reached its destination,
10953 so it would trigger a change event at its previous field location */
10954 if (!player->is_pushing)
10955 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10957 if (!player->active)
10958 RemovePlayer(player);
10961 if (level.use_step_counter)
10971 if (TimeLeft <= 10 && setup.time_limit)
10972 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10974 DrawGameValue_Time(TimeLeft);
10976 if (!TimeLeft && setup.time_limit)
10977 for (i = 0; i < MAX_PLAYERS; i++)
10978 KillPlayer(&stored_player[i]);
10980 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10981 DrawGameValue_Time(TimePlayed);
10984 if (tape.single_step && tape.recording && !tape.pausing &&
10985 !player->programmed_action)
10986 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10990 void ScrollScreen(struct PlayerInfo *player, int mode)
10992 static unsigned long screen_frame_counter = 0;
10994 if (mode == SCROLL_INIT)
10996 /* set scrolling step size according to actual player's moving speed */
10997 ScrollStepSize = TILEX / player->move_delay_value;
10999 screen_frame_counter = FrameCounter;
11000 ScreenMovDir = player->MovDir;
11001 ScreenMovPos = player->MovPos;
11002 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11005 else if (!FrameReached(&screen_frame_counter, 1))
11010 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11011 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11012 redraw_mask |= REDRAW_FIELD;
11015 ScreenMovDir = MV_NONE;
11018 void TestIfPlayerTouchesCustomElement(int x, int y)
11020 static int xy[4][2] =
11027 static int trigger_sides[4][2] =
11029 /* center side border side */
11030 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11031 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11032 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11033 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11035 static int touch_dir[4] =
11037 MV_LEFT | MV_RIGHT,
11042 int center_element = Feld[x][y]; /* should always be non-moving! */
11045 for (i = 0; i < NUM_DIRECTIONS; i++)
11047 int xx = x + xy[i][0];
11048 int yy = y + xy[i][1];
11049 int center_side = trigger_sides[i][0];
11050 int border_side = trigger_sides[i][1];
11051 int border_element;
11053 if (!IN_LEV_FIELD(xx, yy))
11056 if (IS_PLAYER(x, y))
11058 struct PlayerInfo *player = PLAYERINFO(x, y);
11060 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11061 border_element = Feld[xx][yy]; /* may be moving! */
11062 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11063 border_element = Feld[xx][yy];
11064 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11065 border_element = MovingOrBlocked2Element(xx, yy);
11067 continue; /* center and border element do not touch */
11069 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11070 player->index_bit, border_side);
11071 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11072 CE_PLAYER_TOUCHES_X,
11073 player->index_bit, border_side);
11075 else if (IS_PLAYER(xx, yy))
11077 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11079 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11081 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11082 continue; /* center and border element do not touch */
11085 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11086 player->index_bit, center_side);
11087 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11088 CE_PLAYER_TOUCHES_X,
11089 player->index_bit, center_side);
11095 #if USE_ELEMENT_TOUCHING_BUGFIX
11097 void TestIfElementTouchesCustomElement(int x, int y)
11099 static int xy[4][2] =
11106 static int trigger_sides[4][2] =
11108 /* center side border side */
11109 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11110 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11111 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11112 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11114 static int touch_dir[4] =
11116 MV_LEFT | MV_RIGHT,
11121 boolean change_center_element = FALSE;
11122 int center_element = Feld[x][y]; /* should always be non-moving! */
11123 int border_element_old[NUM_DIRECTIONS];
11126 for (i = 0; i < NUM_DIRECTIONS; i++)
11128 int xx = x + xy[i][0];
11129 int yy = y + xy[i][1];
11130 int border_element;
11132 border_element_old[i] = -1;
11134 if (!IN_LEV_FIELD(xx, yy))
11137 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11138 border_element = Feld[xx][yy]; /* may be moving! */
11139 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11140 border_element = Feld[xx][yy];
11141 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11142 border_element = MovingOrBlocked2Element(xx, yy);
11144 continue; /* center and border element do not touch */
11146 border_element_old[i] = border_element;
11149 for (i = 0; i < NUM_DIRECTIONS; i++)
11151 int xx = x + xy[i][0];
11152 int yy = y + xy[i][1];
11153 int center_side = trigger_sides[i][0];
11154 int border_element = border_element_old[i];
11156 if (border_element == -1)
11159 /* check for change of border element */
11160 CheckElementChangeBySide(xx, yy, border_element, center_element,
11161 CE_TOUCHING_X, center_side);
11164 for (i = 0; i < NUM_DIRECTIONS; i++)
11166 int border_side = trigger_sides[i][1];
11167 int border_element = border_element_old[i];
11169 if (border_element == -1)
11172 /* check for change of center element (but change it only once) */
11173 if (!change_center_element)
11174 change_center_element =
11175 CheckElementChangeBySide(x, y, center_element, border_element,
11176 CE_TOUCHING_X, border_side);
11182 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11184 static int xy[4][2] =
11191 static int trigger_sides[4][2] =
11193 /* center side border side */
11194 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11195 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11196 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11197 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11199 static int touch_dir[4] =
11201 MV_LEFT | MV_RIGHT,
11206 boolean change_center_element = FALSE;
11207 int center_element = Feld[x][y]; /* should always be non-moving! */
11210 for (i = 0; i < NUM_DIRECTIONS; i++)
11212 int xx = x + xy[i][0];
11213 int yy = y + xy[i][1];
11214 int center_side = trigger_sides[i][0];
11215 int border_side = trigger_sides[i][1];
11216 int border_element;
11218 if (!IN_LEV_FIELD(xx, yy))
11221 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11222 border_element = Feld[xx][yy]; /* may be moving! */
11223 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11224 border_element = Feld[xx][yy];
11225 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11226 border_element = MovingOrBlocked2Element(xx, yy);
11228 continue; /* center and border element do not touch */
11230 /* check for change of center element (but change it only once) */
11231 if (!change_center_element)
11232 change_center_element =
11233 CheckElementChangeBySide(x, y, center_element, border_element,
11234 CE_TOUCHING_X, border_side);
11236 /* check for change of border element */
11237 CheckElementChangeBySide(xx, yy, border_element, center_element,
11238 CE_TOUCHING_X, center_side);
11244 void TestIfElementHitsCustomElement(int x, int y, int direction)
11246 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11247 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11248 int hitx = x + dx, hity = y + dy;
11249 int hitting_element = Feld[x][y];
11250 int touched_element;
11252 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11255 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11256 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11258 if (IN_LEV_FIELD(hitx, hity))
11260 int opposite_direction = MV_DIR_OPPOSITE(direction);
11261 int hitting_side = direction;
11262 int touched_side = opposite_direction;
11263 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11264 MovDir[hitx][hity] != direction ||
11265 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11271 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11272 CE_HITTING_X, touched_side);
11274 CheckElementChangeBySide(hitx, hity, touched_element,
11275 hitting_element, CE_HIT_BY_X, hitting_side);
11277 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11278 CE_HIT_BY_SOMETHING, opposite_direction);
11282 /* "hitting something" is also true when hitting the playfield border */
11283 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11284 CE_HITTING_SOMETHING, direction);
11288 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11290 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11291 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11292 int hitx = x + dx, hity = y + dy;
11293 int hitting_element = Feld[x][y];
11294 int touched_element;
11296 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11297 !IS_FREE(hitx, hity) &&
11298 (!IS_MOVING(hitx, hity) ||
11299 MovDir[hitx][hity] != direction ||
11300 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11303 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11307 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11311 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11312 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11314 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11315 EP_CAN_SMASH_EVERYTHING, direction);
11317 if (IN_LEV_FIELD(hitx, hity))
11319 int opposite_direction = MV_DIR_OPPOSITE(direction);
11320 int hitting_side = direction;
11321 int touched_side = opposite_direction;
11323 int touched_element = MovingOrBlocked2Element(hitx, hity);
11326 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11327 MovDir[hitx][hity] != direction ||
11328 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11337 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11338 CE_SMASHED_BY_SOMETHING, opposite_direction);
11340 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11341 CE_OTHER_IS_SMASHING, touched_side);
11343 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11344 CE_OTHER_GETS_SMASHED, hitting_side);
11350 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11352 int i, kill_x = -1, kill_y = -1;
11354 int bad_element = -1;
11355 static int test_xy[4][2] =
11362 static int test_dir[4] =
11370 for (i = 0; i < NUM_DIRECTIONS; i++)
11372 int test_x, test_y, test_move_dir, test_element;
11374 test_x = good_x + test_xy[i][0];
11375 test_y = good_y + test_xy[i][1];
11377 if (!IN_LEV_FIELD(test_x, test_y))
11381 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11383 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11385 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11386 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11388 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11389 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11393 bad_element = test_element;
11399 if (kill_x != -1 || kill_y != -1)
11401 if (IS_PLAYER(good_x, good_y))
11403 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11405 if (player->shield_deadly_time_left > 0 &&
11406 !IS_INDESTRUCTIBLE(bad_element))
11407 Bang(kill_x, kill_y);
11408 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11409 KillPlayer(player);
11412 Bang(good_x, good_y);
11416 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11418 int i, kill_x = -1, kill_y = -1;
11419 int bad_element = Feld[bad_x][bad_y];
11420 static int test_xy[4][2] =
11427 static int touch_dir[4] =
11429 MV_LEFT | MV_RIGHT,
11434 static int test_dir[4] =
11442 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11445 for (i = 0; i < NUM_DIRECTIONS; i++)
11447 int test_x, test_y, test_move_dir, test_element;
11449 test_x = bad_x + test_xy[i][0];
11450 test_y = bad_y + test_xy[i][1];
11451 if (!IN_LEV_FIELD(test_x, test_y))
11455 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11457 test_element = Feld[test_x][test_y];
11459 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11460 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11462 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11463 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11465 /* good thing is player or penguin that does not move away */
11466 if (IS_PLAYER(test_x, test_y))
11468 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11470 if (bad_element == EL_ROBOT && player->is_moving)
11471 continue; /* robot does not kill player if he is moving */
11473 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11475 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11476 continue; /* center and border element do not touch */
11483 else if (test_element == EL_PENGUIN)
11492 if (kill_x != -1 || kill_y != -1)
11494 if (IS_PLAYER(kill_x, kill_y))
11496 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11498 if (player->shield_deadly_time_left > 0 &&
11499 !IS_INDESTRUCTIBLE(bad_element))
11500 Bang(bad_x, bad_y);
11501 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11502 KillPlayer(player);
11505 Bang(kill_x, kill_y);
11509 void TestIfPlayerTouchesBadThing(int x, int y)
11511 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11514 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11516 TestIfGoodThingHitsBadThing(x, y, move_dir);
11519 void TestIfBadThingTouchesPlayer(int x, int y)
11521 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11524 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11526 TestIfBadThingHitsGoodThing(x, y, move_dir);
11529 void TestIfFriendTouchesBadThing(int x, int y)
11531 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11534 void TestIfBadThingTouchesFriend(int x, int y)
11536 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11539 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11541 int i, kill_x = bad_x, kill_y = bad_y;
11542 static int xy[4][2] =
11550 for (i = 0; i < NUM_DIRECTIONS; i++)
11554 x = bad_x + xy[i][0];
11555 y = bad_y + xy[i][1];
11556 if (!IN_LEV_FIELD(x, y))
11559 element = Feld[x][y];
11560 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11561 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11569 if (kill_x != bad_x || kill_y != bad_y)
11570 Bang(bad_x, bad_y);
11573 void KillPlayer(struct PlayerInfo *player)
11575 int jx = player->jx, jy = player->jy;
11577 if (!player->active)
11580 /* remove accessible field at the player's position */
11581 Feld[jx][jy] = EL_EMPTY;
11583 /* deactivate shield (else Bang()/Explode() would not work right) */
11584 player->shield_normal_time_left = 0;
11585 player->shield_deadly_time_left = 0;
11588 BuryPlayer(player);
11591 static void KillPlayerUnlessEnemyProtected(int x, int y)
11593 if (!PLAYER_ENEMY_PROTECTED(x, y))
11594 KillPlayer(PLAYERINFO(x, y));
11597 static void KillPlayerUnlessExplosionProtected(int x, int y)
11599 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11600 KillPlayer(PLAYERINFO(x, y));
11603 void BuryPlayer(struct PlayerInfo *player)
11605 int jx = player->jx, jy = player->jy;
11607 if (!player->active)
11610 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11611 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11613 player->GameOver = TRUE;
11614 RemovePlayer(player);
11617 void RemovePlayer(struct PlayerInfo *player)
11619 int jx = player->jx, jy = player->jy;
11620 int i, found = FALSE;
11622 player->present = FALSE;
11623 player->active = FALSE;
11625 if (!ExplodeField[jx][jy])
11626 StorePlayer[jx][jy] = 0;
11628 if (player->is_moving)
11629 DrawLevelField(player->last_jx, player->last_jy);
11631 for (i = 0; i < MAX_PLAYERS; i++)
11632 if (stored_player[i].active)
11636 AllPlayersGone = TRUE;
11642 #if USE_NEW_SNAP_DELAY
11643 static void setFieldForSnapping(int x, int y, int element, int direction)
11645 struct ElementInfo *ei = &element_info[element];
11646 int direction_bit = MV_DIR_TO_BIT(direction);
11647 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11648 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11649 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11651 Feld[x][y] = EL_ELEMENT_SNAPPING;
11652 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11654 ResetGfxAnimation(x, y);
11656 GfxElement[x][y] = element;
11657 GfxAction[x][y] = action;
11658 GfxDir[x][y] = direction;
11659 GfxFrame[x][y] = -1;
11664 =============================================================================
11665 checkDiagonalPushing()
11666 -----------------------------------------------------------------------------
11667 check if diagonal input device direction results in pushing of object
11668 (by checking if the alternative direction is walkable, diggable, ...)
11669 =============================================================================
11672 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11673 int x, int y, int real_dx, int real_dy)
11675 int jx, jy, dx, dy, xx, yy;
11677 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11680 /* diagonal direction: check alternative direction */
11685 xx = jx + (dx == 0 ? real_dx : 0);
11686 yy = jy + (dy == 0 ? real_dy : 0);
11688 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11692 =============================================================================
11694 -----------------------------------------------------------------------------
11695 x, y: field next to player (non-diagonal) to try to dig to
11696 real_dx, real_dy: direction as read from input device (can be diagonal)
11697 =============================================================================
11700 int DigField(struct PlayerInfo *player,
11701 int oldx, int oldy, int x, int y,
11702 int real_dx, int real_dy, int mode)
11704 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11705 boolean player_was_pushing = player->is_pushing;
11706 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11707 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11708 int jx = oldx, jy = oldy;
11709 int dx = x - jx, dy = y - jy;
11710 int nextx = x + dx, nexty = y + dy;
11711 int move_direction = (dx == -1 ? MV_LEFT :
11712 dx == +1 ? MV_RIGHT :
11714 dy == +1 ? MV_DOWN : MV_NONE);
11715 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11716 int dig_side = MV_DIR_OPPOSITE(move_direction);
11717 int old_element = Feld[jx][jy];
11718 #if USE_FIXED_DONT_RUN_INTO
11719 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11725 if (is_player) /* function can also be called by EL_PENGUIN */
11727 if (player->MovPos == 0)
11729 player->is_digging = FALSE;
11730 player->is_collecting = FALSE;
11733 if (player->MovPos == 0) /* last pushing move finished */
11734 player->is_pushing = FALSE;
11736 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11738 player->is_switching = FALSE;
11739 player->push_delay = -1;
11741 return MP_NO_ACTION;
11745 #if !USE_FIXED_DONT_RUN_INTO
11746 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11747 return MP_NO_ACTION;
11750 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11751 old_element = Back[jx][jy];
11753 /* in case of element dropped at player position, check background */
11754 else if (Back[jx][jy] != EL_EMPTY &&
11755 game.engine_version >= VERSION_IDENT(2,2,0,0))
11756 old_element = Back[jx][jy];
11758 /* checking here causes player to move into acid even if the current field
11759 cannot be left to that direction */
11761 #if USE_FIXED_DONT_RUN_INTO
11762 if (player_can_move && DONT_RUN_INTO(element))
11764 if (element == EL_ACID && dx == 0 && dy == 1)
11767 Feld[jx][jy] = EL_PLAYER_1;
11768 InitMovingField(jx, jy, MV_DOWN);
11769 Store[jx][jy] = EL_ACID;
11770 ContinueMoving(jx, jy);
11771 BuryPlayer(player);
11774 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11776 return MP_DONT_RUN_INTO;
11781 #if 1 /* ------------------------------ NEW ------------------------------ */
11783 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11784 return MP_NO_ACTION; /* field has no opening in this direction */
11786 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11787 return MP_NO_ACTION; /* field has no opening in this direction */
11789 #if USE_FIXED_DONT_RUN_INTO
11790 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11794 Feld[jx][jy] = player->artwork_element;
11796 Feld[jx][jy] = EL_PLAYER_1;
11798 InitMovingField(jx, jy, MV_DOWN);
11799 Store[jx][jy] = EL_ACID;
11800 ContinueMoving(jx, jy);
11801 BuryPlayer(player);
11803 return MP_DONT_RUN_INTO;
11807 #if USE_FIXED_DONT_RUN_INTO
11808 if (player_can_move && DONT_RUN_INTO(element))
11810 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11812 return MP_DONT_RUN_INTO;
11816 #else /* ------------------------------ OLD ------------------------------ */
11819 #if USE_FIXED_DONT_RUN_INTO
11820 if (player_can_move && DONT_RUN_INTO(element))
11822 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11824 return MP_DONT_RUN_INTO;
11829 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11830 return MP_NO_ACTION; /* field has no opening in this direction */
11832 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11833 return MP_NO_ACTION; /* field has no opening in this direction */
11835 /* checking here causes player to explode when moving into acid */
11837 #if USE_FIXED_DONT_RUN_INTO
11838 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11841 Feld[jx][jy] = EL_PLAYER_1;
11842 InitMovingField(jx, jy, MV_DOWN);
11843 Store[jx][jy] = EL_ACID;
11844 ContinueMoving(jx, jy);
11845 BuryPlayer(player);
11847 return MP_DONT_RUN_INTO;
11852 #endif /* ------------------------------ END ------------------------------ */
11855 #if USE_FIXED_DONT_RUN_INTO
11856 if (player_can_move && DONT_RUN_INTO(element))
11858 if (element == EL_ACID && dx == 0 && dy == 1)
11861 Feld[jx][jy] = EL_PLAYER_1;
11862 InitMovingField(jx, jy, MV_DOWN);
11863 Store[jx][jy] = EL_ACID;
11864 ContinueMoving(jx, jy);
11865 BuryPlayer(player);
11868 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11870 return MP_DONT_RUN_INTO;
11875 #if USE_FIXED_DONT_RUN_INTO
11876 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11877 return MP_NO_ACTION;
11880 #if !USE_FIXED_DONT_RUN_INTO
11881 element = Feld[x][y];
11884 collect_count = element_info[element].collect_count_initial;
11886 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11887 return MP_NO_ACTION;
11889 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11890 player_can_move = player_can_move_or_snap;
11892 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11893 game.engine_version >= VERSION_IDENT(2,2,0,0))
11895 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11896 player->index_bit, dig_side);
11897 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11898 player->index_bit, dig_side);
11900 if (Feld[x][y] != element) /* field changed by snapping */
11903 return MP_NO_ACTION;
11906 #if USE_PLAYER_GRAVITY
11907 if (player->gravity && is_player && !player->is_auto_moving &&
11908 canFallDown(player) && move_direction != MV_DOWN &&
11909 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11910 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11912 if (game.gravity && is_player && !player->is_auto_moving &&
11913 canFallDown(player) && move_direction != MV_DOWN &&
11914 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11915 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11918 if (player_can_move &&
11919 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11921 int sound_element = SND_ELEMENT(element);
11922 int sound_action = ACTION_WALKING;
11924 if (IS_RND_GATE(element))
11926 if (!player->key[RND_GATE_NR(element)])
11927 return MP_NO_ACTION;
11929 else if (IS_RND_GATE_GRAY(element))
11931 if (!player->key[RND_GATE_GRAY_NR(element)])
11932 return MP_NO_ACTION;
11934 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11936 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11937 return MP_NO_ACTION;
11939 else if (element == EL_EXIT_OPEN ||
11940 element == EL_SP_EXIT_OPEN ||
11941 element == EL_SP_EXIT_OPENING)
11943 sound_action = ACTION_PASSING; /* player is passing exit */
11945 else if (element == EL_EMPTY)
11947 sound_action = ACTION_MOVING; /* nothing to walk on */
11950 /* play sound from background or player, whatever is available */
11951 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11952 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11954 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11956 else if (player_can_move &&
11957 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11959 if (!ACCESS_FROM(element, opposite_direction))
11960 return MP_NO_ACTION; /* field not accessible from this direction */
11962 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11963 return MP_NO_ACTION;
11965 if (IS_EM_GATE(element))
11967 if (!player->key[EM_GATE_NR(element)])
11968 return MP_NO_ACTION;
11970 else if (IS_EM_GATE_GRAY(element))
11972 if (!player->key[EM_GATE_GRAY_NR(element)])
11973 return MP_NO_ACTION;
11975 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11977 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11978 return MP_NO_ACTION;
11980 else if (IS_EMC_GATE(element))
11982 if (!player->key[EMC_GATE_NR(element)])
11983 return MP_NO_ACTION;
11985 else if (IS_EMC_GATE_GRAY(element))
11987 if (!player->key[EMC_GATE_GRAY_NR(element)])
11988 return MP_NO_ACTION;
11990 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11992 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11993 return MP_NO_ACTION;
11995 else if (IS_SP_PORT(element))
11997 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11998 element == EL_SP_GRAVITY_PORT_RIGHT ||
11999 element == EL_SP_GRAVITY_PORT_UP ||
12000 element == EL_SP_GRAVITY_PORT_DOWN)
12001 #if USE_PLAYER_GRAVITY
12002 player->gravity = !player->gravity;
12004 game.gravity = !game.gravity;
12006 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12007 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12008 element == EL_SP_GRAVITY_ON_PORT_UP ||
12009 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12010 #if USE_PLAYER_GRAVITY
12011 player->gravity = TRUE;
12013 game.gravity = TRUE;
12015 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12016 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12017 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12018 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12019 #if USE_PLAYER_GRAVITY
12020 player->gravity = FALSE;
12022 game.gravity = FALSE;
12026 /* automatically move to the next field with double speed */
12027 player->programmed_action = move_direction;
12029 if (player->move_delay_reset_counter == 0)
12031 player->move_delay_reset_counter = 2; /* two double speed steps */
12033 DOUBLE_PLAYER_SPEED(player);
12036 PlayLevelSoundAction(x, y, ACTION_PASSING);
12038 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12042 if (mode != DF_SNAP)
12044 GfxElement[x][y] = GFX_ELEMENT(element);
12045 player->is_digging = TRUE;
12048 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12050 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12051 player->index_bit, dig_side);
12053 if (mode == DF_SNAP)
12055 #if USE_NEW_SNAP_DELAY
12056 if (level.block_snap_field)
12057 setFieldForSnapping(x, y, element, move_direction);
12059 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12061 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12064 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12065 player->index_bit, dig_side);
12068 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12072 if (is_player && mode != DF_SNAP)
12074 GfxElement[x][y] = element;
12075 player->is_collecting = TRUE;
12078 if (element == EL_SPEED_PILL)
12080 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12082 else if (element == EL_EXTRA_TIME && level.time > 0)
12084 TimeLeft += level.extra_time;
12085 DrawGameValue_Time(TimeLeft);
12087 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12089 player->shield_normal_time_left += level.shield_normal_time;
12090 if (element == EL_SHIELD_DEADLY)
12091 player->shield_deadly_time_left += level.shield_deadly_time;
12093 else if (element == EL_DYNAMITE ||
12094 element == EL_EM_DYNAMITE ||
12095 element == EL_SP_DISK_RED)
12097 if (player->inventory_size < MAX_INVENTORY_SIZE)
12098 player->inventory_element[player->inventory_size++] = element;
12101 DrawGameDoorValues();
12103 DrawGameValue_Dynamite(local_player->inventory_size);
12106 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12108 player->dynabomb_count++;
12109 player->dynabombs_left++;
12111 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12113 player->dynabomb_size++;
12115 else if (element == EL_DYNABOMB_INCREASE_POWER)
12117 player->dynabomb_xl = TRUE;
12119 else if (IS_KEY(element))
12121 player->key[KEY_NR(element)] = TRUE;
12124 DrawGameDoorValues();
12126 DrawGameValue_Keys(player->key);
12129 redraw_mask |= REDRAW_DOOR_1;
12131 else if (IS_ENVELOPE(element))
12133 player->show_envelope = element;
12135 else if (element == EL_EMC_LENSES)
12137 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12139 RedrawAllInvisibleElementsForLenses();
12141 else if (element == EL_EMC_MAGNIFIER)
12143 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12145 RedrawAllInvisibleElementsForMagnifier();
12147 else if (IS_DROPPABLE(element) ||
12148 IS_THROWABLE(element)) /* can be collected and dropped */
12152 if (collect_count == 0)
12153 player->inventory_infinite_element = element;
12155 for (i = 0; i < collect_count; i++)
12156 if (player->inventory_size < MAX_INVENTORY_SIZE)
12157 player->inventory_element[player->inventory_size++] = element;
12160 DrawGameDoorValues();
12162 DrawGameValue_Dynamite(local_player->inventory_size);
12165 else if (collect_count > 0)
12167 local_player->gems_still_needed -= collect_count;
12168 if (local_player->gems_still_needed < 0)
12169 local_player->gems_still_needed = 0;
12171 DrawGameValue_Emeralds(local_player->gems_still_needed);
12174 RaiseScoreElement(element);
12175 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12178 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12179 player->index_bit, dig_side);
12181 if (mode == DF_SNAP)
12183 #if USE_NEW_SNAP_DELAY
12184 if (level.block_snap_field)
12185 setFieldForSnapping(x, y, element, move_direction);
12187 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12189 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12193 player->index_bit, dig_side);
12196 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12198 if (mode == DF_SNAP && element != EL_BD_ROCK)
12199 return MP_NO_ACTION;
12201 if (CAN_FALL(element) && dy)
12202 return MP_NO_ACTION;
12204 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12205 !(element == EL_SPRING && level.use_spring_bug))
12206 return MP_NO_ACTION;
12208 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12209 ((move_direction & MV_VERTICAL &&
12210 ((element_info[element].move_pattern & MV_LEFT &&
12211 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12212 (element_info[element].move_pattern & MV_RIGHT &&
12213 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12214 (move_direction & MV_HORIZONTAL &&
12215 ((element_info[element].move_pattern & MV_UP &&
12216 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12217 (element_info[element].move_pattern & MV_DOWN &&
12218 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12219 return MP_NO_ACTION;
12221 /* do not push elements already moving away faster than player */
12222 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12223 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12224 return MP_NO_ACTION;
12226 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12228 if (player->push_delay_value == -1 || !player_was_pushing)
12229 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12231 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12233 if (player->push_delay_value == -1)
12234 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12236 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12238 if (!player->is_pushing)
12239 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12242 player->is_pushing = TRUE;
12243 player->is_active = TRUE;
12245 if (!(IN_LEV_FIELD(nextx, nexty) &&
12246 (IS_FREE(nextx, nexty) ||
12247 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12248 IS_SB_ELEMENT(element)))))
12249 return MP_NO_ACTION;
12251 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12252 return MP_NO_ACTION;
12254 if (player->push_delay == -1) /* new pushing; restart delay */
12255 player->push_delay = 0;
12257 if (player->push_delay < player->push_delay_value &&
12258 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12259 element != EL_SPRING && element != EL_BALLOON)
12261 /* make sure that there is no move delay before next try to push */
12262 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12263 player->move_delay = 0;
12265 return MP_NO_ACTION;
12268 if (IS_SB_ELEMENT(element))
12270 if (element == EL_SOKOBAN_FIELD_FULL)
12272 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12273 local_player->sokobanfields_still_needed++;
12276 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12278 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12279 local_player->sokobanfields_still_needed--;
12282 Feld[x][y] = EL_SOKOBAN_OBJECT;
12284 if (Back[x][y] == Back[nextx][nexty])
12285 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12286 else if (Back[x][y] != 0)
12287 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12290 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12293 if (local_player->sokobanfields_still_needed == 0 &&
12294 game.emulation == EMU_SOKOBAN)
12296 player->LevelSolved = player->GameOver = TRUE;
12297 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12301 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12303 InitMovingField(x, y, move_direction);
12304 GfxAction[x][y] = ACTION_PUSHING;
12306 if (mode == DF_SNAP)
12307 ContinueMoving(x, y);
12309 MovPos[x][y] = (dx != 0 ? dx : dy);
12311 Pushed[x][y] = TRUE;
12312 Pushed[nextx][nexty] = TRUE;
12314 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12315 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12317 player->push_delay_value = -1; /* get new value later */
12319 /* check for element change _after_ element has been pushed */
12320 if (game.use_change_when_pushing_bug)
12322 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12323 player->index_bit, dig_side);
12324 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12325 player->index_bit, dig_side);
12328 else if (IS_SWITCHABLE(element))
12330 if (PLAYER_SWITCHING(player, x, y))
12332 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12333 player->index_bit, dig_side);
12338 player->is_switching = TRUE;
12339 player->switch_x = x;
12340 player->switch_y = y;
12342 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12344 if (element == EL_ROBOT_WHEEL)
12346 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12350 DrawLevelField(x, y);
12352 else if (element == EL_SP_TERMINAL)
12357 SCAN_PLAYFIELD(xx, yy)
12359 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12362 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12364 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12365 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12368 else if (IS_BELT_SWITCH(element))
12370 ToggleBeltSwitch(x, y);
12372 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12373 element == EL_SWITCHGATE_SWITCH_DOWN)
12375 ToggleSwitchgateSwitch(x, y);
12377 else if (element == EL_LIGHT_SWITCH ||
12378 element == EL_LIGHT_SWITCH_ACTIVE)
12380 ToggleLightSwitch(x, y);
12382 else if (element == EL_TIMEGATE_SWITCH)
12384 ActivateTimegateSwitch(x, y);
12386 else if (element == EL_BALLOON_SWITCH_LEFT ||
12387 element == EL_BALLOON_SWITCH_RIGHT ||
12388 element == EL_BALLOON_SWITCH_UP ||
12389 element == EL_BALLOON_SWITCH_DOWN ||
12390 element == EL_BALLOON_SWITCH_NONE ||
12391 element == EL_BALLOON_SWITCH_ANY)
12393 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12394 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12395 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12396 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12397 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12400 else if (element == EL_LAMP)
12402 Feld[x][y] = EL_LAMP_ACTIVE;
12403 local_player->lights_still_needed--;
12405 ResetGfxAnimation(x, y);
12406 DrawLevelField(x, y);
12408 else if (element == EL_TIME_ORB_FULL)
12410 Feld[x][y] = EL_TIME_ORB_EMPTY;
12412 if (level.time > 0 || level.use_time_orb_bug)
12414 TimeLeft += level.time_orb_time;
12415 DrawGameValue_Time(TimeLeft);
12418 ResetGfxAnimation(x, y);
12419 DrawLevelField(x, y);
12421 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12422 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12426 game.ball_state = !game.ball_state;
12429 SCAN_PLAYFIELD(xx, yy)
12431 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12434 int e = Feld[xx][yy];
12436 if (game.ball_state)
12438 if (e == EL_EMC_MAGIC_BALL)
12439 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12440 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12441 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12445 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12446 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12447 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12448 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12453 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12454 player->index_bit, dig_side);
12456 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12457 player->index_bit, dig_side);
12459 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12460 player->index_bit, dig_side);
12466 if (!PLAYER_SWITCHING(player, x, y))
12468 player->is_switching = TRUE;
12469 player->switch_x = x;
12470 player->switch_y = y;
12472 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12473 player->index_bit, dig_side);
12474 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12475 player->index_bit, dig_side);
12477 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12478 player->index_bit, dig_side);
12479 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12480 player->index_bit, dig_side);
12483 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12484 player->index_bit, dig_side);
12485 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12486 player->index_bit, dig_side);
12488 return MP_NO_ACTION;
12491 player->push_delay = -1;
12493 if (is_player) /* function can also be called by EL_PENGUIN */
12495 if (Feld[x][y] != element) /* really digged/collected something */
12497 player->is_collecting = !player->is_digging;
12498 player->is_active = TRUE;
12505 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12507 int jx = player->jx, jy = player->jy;
12508 int x = jx + dx, y = jy + dy;
12509 int snap_direction = (dx == -1 ? MV_LEFT :
12510 dx == +1 ? MV_RIGHT :
12512 dy == +1 ? MV_DOWN : MV_NONE);
12513 boolean can_continue_snapping = (level.continuous_snapping &&
12514 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12516 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12519 if (!player->active || !IN_LEV_FIELD(x, y))
12527 if (player->MovPos == 0)
12528 player->is_pushing = FALSE;
12530 player->is_snapping = FALSE;
12532 if (player->MovPos == 0)
12534 player->is_moving = FALSE;
12535 player->is_digging = FALSE;
12536 player->is_collecting = FALSE;
12542 #if USE_NEW_CONTINUOUS_SNAPPING
12543 /* prevent snapping with already pressed snap key when not allowed */
12544 if (player->is_snapping && !can_continue_snapping)
12547 if (player->is_snapping)
12551 player->MovDir = snap_direction;
12553 if (player->MovPos == 0)
12555 player->is_moving = FALSE;
12556 player->is_digging = FALSE;
12557 player->is_collecting = FALSE;
12560 player->is_dropping = FALSE;
12561 player->is_dropping_pressed = FALSE;
12562 player->drop_pressed_delay = 0;
12564 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12567 player->is_snapping = TRUE;
12568 player->is_active = TRUE;
12570 if (player->MovPos == 0)
12572 player->is_moving = FALSE;
12573 player->is_digging = FALSE;
12574 player->is_collecting = FALSE;
12577 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12578 DrawLevelField(player->last_jx, player->last_jy);
12580 DrawLevelField(x, y);
12585 boolean DropElement(struct PlayerInfo *player)
12587 int old_element, new_element;
12588 int dropx = player->jx, dropy = player->jy;
12589 int drop_direction = player->MovDir;
12590 int drop_side = drop_direction;
12591 int drop_element = (player->inventory_size > 0 ?
12592 player->inventory_element[player->inventory_size - 1] :
12593 player->inventory_infinite_element != EL_UNDEFINED ?
12594 player->inventory_infinite_element :
12595 player->dynabombs_left > 0 ?
12596 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12599 player->is_dropping_pressed = TRUE;
12601 /* do not drop an element on top of another element; when holding drop key
12602 pressed without moving, dropped element must move away before the next
12603 element can be dropped (this is especially important if the next element
12604 is dynamite, which can be placed on background for historical reasons) */
12605 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12608 if (IS_THROWABLE(drop_element))
12610 dropx += GET_DX_FROM_DIR(drop_direction);
12611 dropy += GET_DY_FROM_DIR(drop_direction);
12613 if (!IN_LEV_FIELD(dropx, dropy))
12617 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12618 new_element = drop_element; /* default: no change when dropping */
12620 /* check if player is active, not moving and ready to drop */
12621 if (!player->active || player->MovPos || player->drop_delay > 0)
12624 /* check if player has anything that can be dropped */
12625 if (new_element == EL_UNDEFINED)
12628 /* check if drop key was pressed long enough for EM style dynamite */
12629 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12632 /* check if anything can be dropped at the current position */
12633 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12636 /* collected custom elements can only be dropped on empty fields */
12637 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12640 if (old_element != EL_EMPTY)
12641 Back[dropx][dropy] = old_element; /* store old element on this field */
12643 ResetGfxAnimation(dropx, dropy);
12644 ResetRandomAnimationValue(dropx, dropy);
12646 if (player->inventory_size > 0 ||
12647 player->inventory_infinite_element != EL_UNDEFINED)
12649 if (player->inventory_size > 0)
12651 player->inventory_size--;
12654 DrawGameDoorValues();
12656 DrawGameValue_Dynamite(local_player->inventory_size);
12659 if (new_element == EL_DYNAMITE)
12660 new_element = EL_DYNAMITE_ACTIVE;
12661 else if (new_element == EL_EM_DYNAMITE)
12662 new_element = EL_EM_DYNAMITE_ACTIVE;
12663 else if (new_element == EL_SP_DISK_RED)
12664 new_element = EL_SP_DISK_RED_ACTIVE;
12667 Feld[dropx][dropy] = new_element;
12669 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12670 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12671 el2img(Feld[dropx][dropy]), 0);
12673 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12675 /* needed if previous element just changed to "empty" in the last frame */
12676 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12678 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12679 player->index_bit, drop_side);
12680 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12682 player->index_bit, drop_side);
12684 TestIfElementTouchesCustomElement(dropx, dropy);
12686 else /* player is dropping a dyna bomb */
12688 player->dynabombs_left--;
12690 Feld[dropx][dropy] = new_element;
12692 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12693 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12694 el2img(Feld[dropx][dropy]), 0);
12696 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12699 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12700 InitField_WithBug1(dropx, dropy, FALSE);
12702 new_element = Feld[dropx][dropy]; /* element might have changed */
12704 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12705 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12707 int move_direction, nextx, nexty;
12709 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12710 MovDir[dropx][dropy] = drop_direction;
12712 move_direction = MovDir[dropx][dropy];
12713 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12714 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12716 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12717 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12720 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12721 player->is_dropping = TRUE;
12723 player->drop_pressed_delay = 0;
12724 player->is_dropping_pressed = FALSE;
12726 player->drop_x = dropx;
12727 player->drop_y = dropy;
12732 /* ------------------------------------------------------------------------- */
12733 /* game sound playing functions */
12734 /* ------------------------------------------------------------------------- */
12736 static int *loop_sound_frame = NULL;
12737 static int *loop_sound_volume = NULL;
12739 void InitPlayLevelSound()
12741 int num_sounds = getSoundListSize();
12743 checked_free(loop_sound_frame);
12744 checked_free(loop_sound_volume);
12746 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12747 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12750 static void PlayLevelSound(int x, int y, int nr)
12752 int sx = SCREENX(x), sy = SCREENY(y);
12753 int volume, stereo_position;
12754 int max_distance = 8;
12755 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12757 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12758 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12761 if (!IN_LEV_FIELD(x, y) ||
12762 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12763 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12766 volume = SOUND_MAX_VOLUME;
12768 if (!IN_SCR_FIELD(sx, sy))
12770 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12771 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12773 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12776 stereo_position = (SOUND_MAX_LEFT +
12777 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12778 (SCR_FIELDX + 2 * max_distance));
12780 if (IS_LOOP_SOUND(nr))
12782 /* This assures that quieter loop sounds do not overwrite louder ones,
12783 while restarting sound volume comparison with each new game frame. */
12785 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12788 loop_sound_volume[nr] = volume;
12789 loop_sound_frame[nr] = FrameCounter;
12792 PlaySoundExt(nr, volume, stereo_position, type);
12795 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12797 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12798 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12799 y < LEVELY(BY1) ? LEVELY(BY1) :
12800 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12804 static void PlayLevelSoundAction(int x, int y, int action)
12806 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12809 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12811 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12813 if (sound_effect != SND_UNDEFINED)
12814 PlayLevelSound(x, y, sound_effect);
12817 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12820 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12822 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12823 PlayLevelSound(x, y, sound_effect);
12826 static void PlayLevelSoundActionIfLoop(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 PlayLevelSound(x, y, sound_effect);
12834 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12836 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12838 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12839 StopSound(sound_effect);
12842 static void PlayLevelMusic()
12844 if (levelset.music[level_nr] != MUS_UNDEFINED)
12845 PlayMusic(levelset.music[level_nr]); /* from config file */
12847 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12850 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12852 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12857 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12861 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12865 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12869 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12873 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12877 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12881 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12884 case SAMPLE_android_clone:
12885 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12888 case SAMPLE_android_move:
12889 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12892 case SAMPLE_spring:
12893 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12897 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12901 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12904 case SAMPLE_eater_eat:
12905 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12909 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12912 case SAMPLE_collect:
12913 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12916 case SAMPLE_diamond:
12917 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12920 case SAMPLE_squash:
12921 /* !!! CHECK THIS !!! */
12923 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12925 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12929 case SAMPLE_wonderfall:
12930 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12934 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12938 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12942 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12946 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12950 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12954 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12957 case SAMPLE_wonder:
12958 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12962 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12965 case SAMPLE_exit_open:
12966 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12969 case SAMPLE_exit_leave:
12970 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12973 case SAMPLE_dynamite:
12974 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12978 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12982 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12986 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12990 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12994 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12998 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13002 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13007 void RaiseScore(int value)
13009 local_player->score += value;
13011 DrawGameValue_Score(local_player->score);
13014 void RaiseScoreElement(int element)
13019 case EL_BD_DIAMOND:
13020 case EL_EMERALD_YELLOW:
13021 case EL_EMERALD_RED:
13022 case EL_EMERALD_PURPLE:
13023 case EL_SP_INFOTRON:
13024 RaiseScore(level.score[SC_EMERALD]);
13027 RaiseScore(level.score[SC_DIAMOND]);
13030 RaiseScore(level.score[SC_CRYSTAL]);
13033 RaiseScore(level.score[SC_PEARL]);
13036 case EL_BD_BUTTERFLY:
13037 case EL_SP_ELECTRON:
13038 RaiseScore(level.score[SC_BUG]);
13041 case EL_BD_FIREFLY:
13042 case EL_SP_SNIKSNAK:
13043 RaiseScore(level.score[SC_SPACESHIP]);
13046 case EL_DARK_YAMYAM:
13047 RaiseScore(level.score[SC_YAMYAM]);
13050 RaiseScore(level.score[SC_ROBOT]);
13053 RaiseScore(level.score[SC_PACMAN]);
13056 RaiseScore(level.score[SC_NUT]);
13059 case EL_EM_DYNAMITE:
13060 case EL_SP_DISK_RED:
13061 case EL_DYNABOMB_INCREASE_NUMBER:
13062 case EL_DYNABOMB_INCREASE_SIZE:
13063 case EL_DYNABOMB_INCREASE_POWER:
13064 RaiseScore(level.score[SC_DYNAMITE]);
13066 case EL_SHIELD_NORMAL:
13067 case EL_SHIELD_DEADLY:
13068 RaiseScore(level.score[SC_SHIELD]);
13070 case EL_EXTRA_TIME:
13071 RaiseScore(level.extra_time_score);
13085 RaiseScore(level.score[SC_KEY]);
13088 RaiseScore(element_info[element].collect_score);
13093 void RequestQuitGame(boolean ask_if_really_quit)
13095 if (AllPlayersGone ||
13096 !ask_if_really_quit ||
13097 level_editor_test_game ||
13098 Request("Do you really want to quit the game ?",
13099 REQ_ASK | REQ_STAY_CLOSED))
13101 #if defined(NETWORK_AVALIABLE)
13102 if (options.network)
13103 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13107 game_status = GAME_MODE_MAIN;
13113 if (tape.playing && tape.deactivate_display)
13114 TapeDeactivateDisplayOff(TRUE);
13116 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13118 if (tape.playing && tape.deactivate_display)
13119 TapeDeactivateDisplayOn();
13124 /* ---------- new game button stuff ---------------------------------------- */
13126 /* graphic position values for game buttons */
13127 #define GAME_BUTTON_XSIZE 30
13128 #define GAME_BUTTON_YSIZE 30
13129 #define GAME_BUTTON_XPOS 5
13130 #define GAME_BUTTON_YPOS 215
13131 #define SOUND_BUTTON_XPOS 5
13132 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13134 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13135 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13136 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13137 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13138 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13139 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13146 } gamebutton_info[NUM_GAME_BUTTONS] =
13149 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13154 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13155 GAME_CTRL_ID_PAUSE,
13159 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13164 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13165 SOUND_CTRL_ID_MUSIC,
13166 "background music on/off"
13169 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13170 SOUND_CTRL_ID_LOOPS,
13171 "sound loops on/off"
13174 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13175 SOUND_CTRL_ID_SIMPLE,
13176 "normal sounds on/off"
13180 void CreateGameButtons()
13184 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13186 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13187 struct GadgetInfo *gi;
13190 unsigned long event_mask;
13191 int gd_xoffset, gd_yoffset;
13192 int gd_x1, gd_x2, gd_y1, gd_y2;
13195 gd_xoffset = gamebutton_info[i].x;
13196 gd_yoffset = gamebutton_info[i].y;
13197 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13198 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13200 if (id == GAME_CTRL_ID_STOP ||
13201 id == GAME_CTRL_ID_PAUSE ||
13202 id == GAME_CTRL_ID_PLAY)
13204 button_type = GD_TYPE_NORMAL_BUTTON;
13206 event_mask = GD_EVENT_RELEASED;
13207 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13208 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13212 button_type = GD_TYPE_CHECK_BUTTON;
13214 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13215 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13216 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13217 event_mask = GD_EVENT_PRESSED;
13218 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13219 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13222 gi = CreateGadget(GDI_CUSTOM_ID, id,
13223 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13224 GDI_X, DX + gd_xoffset,
13225 GDI_Y, DY + gd_yoffset,
13226 GDI_WIDTH, GAME_BUTTON_XSIZE,
13227 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13228 GDI_TYPE, button_type,
13229 GDI_STATE, GD_BUTTON_UNPRESSED,
13230 GDI_CHECKED, checked,
13231 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13232 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13233 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13234 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13235 GDI_EVENT_MASK, event_mask,
13236 GDI_CALLBACK_ACTION, HandleGameButtons,
13240 Error(ERR_EXIT, "cannot create gadget");
13242 game_gadget[id] = gi;
13246 void FreeGameButtons()
13250 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13251 FreeGadget(game_gadget[i]);
13254 static void MapGameButtons()
13258 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13259 MapGadget(game_gadget[i]);
13262 void UnmapGameButtons()
13266 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13267 UnmapGadget(game_gadget[i]);
13270 static void HandleGameButtons(struct GadgetInfo *gi)
13272 int id = gi->custom_id;
13274 if (game_status != GAME_MODE_PLAYING)
13279 case GAME_CTRL_ID_STOP:
13283 RequestQuitGame(TRUE);
13286 case GAME_CTRL_ID_PAUSE:
13287 if (options.network)
13289 #if defined(NETWORK_AVALIABLE)
13291 SendToServer_ContinuePlaying();
13293 SendToServer_PausePlaying();
13297 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13300 case GAME_CTRL_ID_PLAY:
13303 #if defined(NETWORK_AVALIABLE)
13304 if (options.network)
13305 SendToServer_ContinuePlaying();
13309 tape.pausing = FALSE;
13310 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13315 case SOUND_CTRL_ID_MUSIC:
13316 if (setup.sound_music)
13318 setup.sound_music = FALSE;
13321 else if (audio.music_available)
13323 setup.sound = setup.sound_music = TRUE;
13325 SetAudioMode(setup.sound);
13331 case SOUND_CTRL_ID_LOOPS:
13332 if (setup.sound_loops)
13333 setup.sound_loops = FALSE;
13334 else if (audio.loops_available)
13336 setup.sound = setup.sound_loops = TRUE;
13337 SetAudioMode(setup.sound);
13341 case SOUND_CTRL_ID_SIMPLE:
13342 if (setup.sound_simple)
13343 setup.sound_simple = FALSE;
13344 else if (audio.sound_available)
13346 setup.sound = setup.sound_simple = TRUE;
13347 SetAudioMode(setup.sound);