1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 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)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
62 /* for MovePlayer() */
63 #define MP_NO_ACTION 0
66 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
68 /* for ScrollPlayer() */
70 #define SCROLL_GO_ON 1
72 /* for Bang()/Explode() */
73 #define EX_PHASE_START 0
74 #define EX_TYPE_NONE 0
75 #define EX_TYPE_NORMAL (1 << 0)
76 #define EX_TYPE_CENTER (1 << 1)
77 #define EX_TYPE_BORDER (1 << 2)
78 #define EX_TYPE_CROSS (1 << 3)
79 #define EX_TYPE_DYNA (1 << 4)
80 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
82 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
84 /* special positions in the game control window (relative to control window) */
85 #define XX_LEVEL1 (game.panel.level.x)
86 #define XX_LEVEL2 (game.panel.level.x - 1)
87 #define YY_LEVEL (game.panel.level.y)
88 #define XX_EMERALDS (game.panel.gems.x)
89 #define YY_EMERALDS (game.panel.gems.y)
90 #define XX_DYNAMITE (game.panel.inventory.x)
91 #define YY_DYNAMITE (game.panel.inventory.y)
92 #define XX_KEYS (game.panel.keys.x)
93 #define YY_KEYS (game.panel.keys.y)
94 #define XX_SCORE (game.panel.score.x)
95 #define YY_SCORE (game.panel.score.y)
96 #define XX_TIME1 (game.panel.time.x)
97 #define XX_TIME2 (game.panel.time.x + 1)
98 #define YY_TIME (game.panel.time.y)
100 /* special positions in the game control window (relative to main window) */
101 #define DX_LEVEL1 (DX + XX_LEVEL1)
102 #define DX_LEVEL2 (DX + XX_LEVEL2)
103 #define DY_LEVEL (DY + YY_LEVEL)
104 #define DX_EMERALDS (DX + XX_EMERALDS)
105 #define DY_EMERALDS (DY + YY_EMERALDS)
106 #define DX_DYNAMITE (DX + XX_DYNAMITE)
107 #define DY_DYNAMITE (DY + YY_DYNAMITE)
108 #define DX_KEYS (DX + XX_KEYS)
109 #define DY_KEYS (DY + YY_KEYS)
110 #define DX_SCORE (DX + XX_SCORE)
111 #define DY_SCORE (DY + YY_SCORE)
112 #define DX_TIME1 (DX + XX_TIME1)
113 #define DX_TIME2 (DX + XX_TIME2)
114 #define DY_TIME (DY + YY_TIME)
116 /* values for delayed check of falling and moving elements and for collision */
117 #define CHECK_DELAY_MOVING 3
118 #define CHECK_DELAY_FALLING 3
119 #define CHECK_DELAY_COLLISION 2
121 /* values for initial player move delay (initial delay counter value) */
122 #define INITIAL_MOVE_DELAY_OFF -1
123 #define INITIAL_MOVE_DELAY_ON 0
125 /* values for player movement speed (which is in fact a delay value) */
126 #define MOVE_DELAY_MIN_SPEED 32
127 #define MOVE_DELAY_NORMAL_SPEED 8
128 #define MOVE_DELAY_HIGH_SPEED 4
129 #define MOVE_DELAY_MAX_SPEED 1
131 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
132 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
134 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
135 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
137 /* values for other actions */
138 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
139 #define MOVE_STEPSIZE_MIN (1)
140 #define MOVE_STEPSIZE_MAX (TILEX)
142 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
143 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
145 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
147 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
148 RND(element_info[e].push_delay_random))
149 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
150 RND(element_info[e].drop_delay_random))
151 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
152 RND(element_info[e].move_delay_random))
153 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
154 (element_info[e].move_delay_random))
155 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
156 RND(element_info[e].ce_value_random_initial))
157 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
158 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
159 RND((c)->delay_random * (c)->delay_frames))
160 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
161 RND((c)->delay_random))
164 #define GET_VALID_RUNTIME_ELEMENT(e) \
165 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
167 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
168 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
169 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
170 (be) + (e) - EL_SELF)
172 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
173 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
174 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
175 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
176 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
177 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
178 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
179 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
180 RESOLVED_REFERENCE_ELEMENT(be, e) : \
183 #define CAN_GROW_INTO(e) \
184 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
186 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
187 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
190 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
191 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
192 (CAN_MOVE_INTO_ACID(e) && \
193 Feld[x][y] == EL_ACID) || \
196 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
197 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
198 (CAN_MOVE_INTO_ACID(e) && \
199 Feld[x][y] == EL_ACID) || \
202 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
203 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
207 (DONT_COLLIDE_WITH(e) && \
209 !PLAYER_ENEMY_PROTECTED(x, y))))
211 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
214 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
217 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
220 #define ANDROID_CAN_CLONE_FIELD(x, y) \
221 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
222 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
224 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
230 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
233 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
236 #define PIG_CAN_ENTER_FIELD(e, x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
239 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
240 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
241 IS_FOOD_PENGUIN(Feld[x][y])))
242 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
243 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
245 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
246 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
248 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
249 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
251 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
252 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
253 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
255 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
257 #define CE_ENTER_FIELD_COND(e, x, y) \
258 (!IS_PLAYER(x, y) && \
259 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
261 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
262 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
264 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
265 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
267 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
268 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
269 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
270 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
272 /* game button identifiers */
273 #define GAME_CTRL_ID_STOP 0
274 #define GAME_CTRL_ID_PAUSE 1
275 #define GAME_CTRL_ID_PLAY 2
276 #define SOUND_CTRL_ID_MUSIC 3
277 #define SOUND_CTRL_ID_LOOPS 4
278 #define SOUND_CTRL_ID_SIMPLE 5
280 #define NUM_GAME_BUTTONS 6
283 /* forward declaration for internal use */
285 static void CreateField(int, int, int);
287 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
288 static void AdvanceFrameAndPlayerCounters(int);
290 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
291 static boolean MovePlayer(struct PlayerInfo *, int, int);
292 static void ScrollPlayer(struct PlayerInfo *, int);
293 static void ScrollScreen(struct PlayerInfo *, int);
295 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
297 static void InitBeltMovement(void);
298 static void CloseAllOpenTimegates(void);
299 static void CheckGravityMovement(struct PlayerInfo *);
300 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
301 static void KillPlayerUnlessEnemyProtected(int, int);
302 static void KillPlayerUnlessExplosionProtected(int, int);
304 static void TestIfPlayerTouchesCustomElement(int, int);
305 static void TestIfElementTouchesCustomElement(int, int);
306 static void TestIfElementHitsCustomElement(int, int, int);
308 static void TestIfElementSmashesCustomElement(int, int, int);
311 static void HandleElementChange(int, int, int);
312 static void ExecuteCustomElementAction(int, int, int, int);
313 static boolean ChangeElement(int, int, int, int);
315 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
316 #define CheckTriggeredElementChange(x, y, e, ev) \
317 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
318 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
319 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
320 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
321 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
322 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
323 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
325 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
326 #define CheckElementChange(x, y, e, te, ev) \
327 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
328 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
329 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
330 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
331 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
333 static void PlayLevelSound(int, int, int);
334 static void PlayLevelSoundNearest(int, int, int);
335 static void PlayLevelSoundAction(int, int, int);
336 static void PlayLevelSoundElementAction(int, int, int, int);
337 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
338 static void PlayLevelSoundActionIfLoop(int, int, int);
339 static void StopLevelSoundActionIfLoop(int, int, int);
340 static void PlayLevelMusic();
342 static void MapGameButtons();
343 static void HandleGameButtons(struct GadgetInfo *);
345 int AmoebeNachbarNr(int, int);
346 void AmoebeUmwandeln(int, int);
347 void ContinueMoving(int, int);
349 void InitMovDir(int, int);
350 void InitAmoebaNr(int, int);
351 int NewHiScore(void);
353 void TestIfGoodThingHitsBadThing(int, int, int);
354 void TestIfBadThingHitsGoodThing(int, int, int);
355 void TestIfPlayerTouchesBadThing(int, int);
356 void TestIfPlayerRunsIntoBadThing(int, int, int);
357 void TestIfBadThingTouchesPlayer(int, int);
358 void TestIfBadThingRunsIntoPlayer(int, int, int);
359 void TestIfFriendTouchesBadThing(int, int);
360 void TestIfBadThingTouchesFriend(int, int);
361 void TestIfBadThingTouchesOtherBadThing(int, int);
363 void KillPlayer(struct PlayerInfo *);
364 void BuryPlayer(struct PlayerInfo *);
365 void RemovePlayer(struct PlayerInfo *);
367 boolean SnapField(struct PlayerInfo *, int, int);
368 boolean DropElement(struct PlayerInfo *);
370 static int getInvisibleActiveFromInvisibleElement(int);
371 static int getInvisibleFromInvisibleActiveElement(int);
373 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
376 /* ------------------------------------------------------------------------- */
377 /* definition of elements that automatically change to other elements after */
378 /* a specified time, eventually calling a function when changing */
379 /* ------------------------------------------------------------------------- */
381 /* forward declaration for changer functions */
382 static void InitBuggyBase(int, int);
383 static void WarnBuggyBase(int, int);
385 static void InitTrap(int, int);
386 static void ActivateTrap(int, int);
387 static void ChangeActiveTrap(int, int);
389 static void InitRobotWheel(int, int);
390 static void RunRobotWheel(int, int);
391 static void StopRobotWheel(int, int);
393 static void InitTimegateWheel(int, int);
394 static void RunTimegateWheel(int, int);
396 static void InitMagicBallDelay(int, int);
397 static void ActivateMagicBall(int, int);
399 struct ChangingElementInfo
404 void (*pre_change_function)(int x, int y);
405 void (*change_function)(int x, int y);
406 void (*post_change_function)(int x, int y);
409 static struct ChangingElementInfo change_delay_list[] =
460 EL_SWITCHGATE_OPENING,
468 EL_SWITCHGATE_CLOSING,
469 EL_SWITCHGATE_CLOSED,
501 EL_ACID_SPLASH_RIGHT,
510 EL_SP_BUGGY_BASE_ACTIVATING,
517 EL_SP_BUGGY_BASE_ACTIVATING,
518 EL_SP_BUGGY_BASE_ACTIVE,
525 EL_SP_BUGGY_BASE_ACTIVE,
549 EL_ROBOT_WHEEL_ACTIVE,
557 EL_TIMEGATE_SWITCH_ACTIVE,
565 EL_EMC_MAGIC_BALL_ACTIVE,
566 EL_EMC_MAGIC_BALL_ACTIVE,
573 EL_EMC_SPRING_BUMPER_ACTIVE,
574 EL_EMC_SPRING_BUMPER,
581 EL_DIAGONAL_SHRINKING,
610 int push_delay_fixed, push_delay_random;
615 { EL_BALLOON, 0, 0 },
617 { EL_SOKOBAN_OBJECT, 2, 0 },
618 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
619 { EL_SATELLITE, 2, 0 },
620 { EL_SP_DISK_YELLOW, 2, 0 },
622 { EL_UNDEFINED, 0, 0 },
630 move_stepsize_list[] =
632 { EL_AMOEBA_DROP, 2 },
633 { EL_AMOEBA_DROPPING, 2 },
634 { EL_QUICKSAND_FILLING, 1 },
635 { EL_QUICKSAND_EMPTYING, 1 },
636 { EL_MAGIC_WALL_FILLING, 2 },
637 { EL_BD_MAGIC_WALL_FILLING, 2 },
638 { EL_MAGIC_WALL_EMPTYING, 2 },
639 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
649 collect_count_list[] =
652 { EL_BD_DIAMOND, 1 },
653 { EL_EMERALD_YELLOW, 1 },
654 { EL_EMERALD_RED, 1 },
655 { EL_EMERALD_PURPLE, 1 },
657 { EL_SP_INFOTRON, 1 },
669 access_direction_list[] =
671 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
672 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
673 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
674 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
675 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
676 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
677 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
678 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
679 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
680 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
681 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
683 { EL_SP_PORT_LEFT, MV_RIGHT },
684 { EL_SP_PORT_RIGHT, MV_LEFT },
685 { EL_SP_PORT_UP, MV_DOWN },
686 { EL_SP_PORT_DOWN, MV_UP },
687 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
688 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
689 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
690 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
691 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
692 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
693 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
694 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
695 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
696 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
697 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
698 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
699 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
700 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
701 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
703 { EL_UNDEFINED, MV_NONE }
706 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
708 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
709 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
710 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
711 IS_JUST_CHANGING(x, y))
713 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
715 /* static variables for playfield scan mode (scanning forward or backward) */
716 static int playfield_scan_start_x = 0;
717 static int playfield_scan_start_y = 0;
718 static int playfield_scan_delta_x = 1;
719 static int playfield_scan_delta_y = 1;
721 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
722 (y) >= 0 && (y) <= lev_fieldy - 1; \
723 (y) += playfield_scan_delta_y) \
724 for ((x) = playfield_scan_start_x; \
725 (x) >= 0 && (x) <= lev_fieldx - 1; \
726 (x) += playfield_scan_delta_x) \
729 void DEBUG_SetMaximumDynamite()
733 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
734 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
735 local_player->inventory_element[local_player->inventory_size++] =
740 static void InitPlayfieldScanModeVars()
742 if (game.use_reverse_scan_direction)
744 playfield_scan_start_x = lev_fieldx - 1;
745 playfield_scan_start_y = lev_fieldy - 1;
747 playfield_scan_delta_x = -1;
748 playfield_scan_delta_y = -1;
752 playfield_scan_start_x = 0;
753 playfield_scan_start_y = 0;
755 playfield_scan_delta_x = 1;
756 playfield_scan_delta_y = 1;
760 static void InitPlayfieldScanMode(int mode)
762 game.use_reverse_scan_direction =
763 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
765 InitPlayfieldScanModeVars();
768 static int get_move_delay_from_stepsize(int move_stepsize)
771 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
773 /* make sure that stepsize value is always a power of 2 */
774 move_stepsize = (1 << log_2(move_stepsize));
776 return TILEX / move_stepsize;
779 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
782 int player_nr = player->index_nr;
783 int move_delay = get_move_delay_from_stepsize(move_stepsize);
784 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
786 /* do no immediately change move delay -- the player might just be moving */
787 player->move_delay_value_next = move_delay;
789 /* information if player can move must be set separately */
790 player->cannot_move = cannot_move;
794 player->move_delay = game.initial_move_delay[player_nr];
795 player->move_delay_value = game.initial_move_delay_value[player_nr];
797 player->move_delay_value_next = -1;
799 player->move_delay_reset_counter = 0;
803 void GetPlayerConfig()
805 if (!audio.sound_available)
806 setup.sound_simple = FALSE;
808 if (!audio.loops_available)
809 setup.sound_loops = FALSE;
811 if (!audio.music_available)
812 setup.sound_music = FALSE;
814 if (!video.fullscreen_available)
815 setup.fullscreen = FALSE;
817 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
819 SetAudioMode(setup.sound);
823 static int getBeltNrFromBeltElement(int element)
825 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
826 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
827 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
830 static int getBeltNrFromBeltActiveElement(int element)
832 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
833 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
834 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
837 static int getBeltNrFromBeltSwitchElement(int element)
839 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
840 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
841 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
844 static int getBeltDirNrFromBeltSwitchElement(int element)
846 static int belt_base_element[4] =
848 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
849 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
850 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
851 EL_CONVEYOR_BELT_4_SWITCH_LEFT
854 int belt_nr = getBeltNrFromBeltSwitchElement(element);
855 int belt_dir_nr = element - belt_base_element[belt_nr];
857 return (belt_dir_nr % 3);
860 static int getBeltDirFromBeltSwitchElement(int element)
862 static int belt_move_dir[3] =
869 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
871 return belt_move_dir[belt_dir_nr];
874 static int get_element_from_group_element(int element)
876 if (IS_GROUP_ELEMENT(element))
878 struct ElementGroupInfo *group = element_info[element].group;
879 int last_anim_random_frame = gfx.anim_random_frame;
882 if (group->choice_mode == ANIM_RANDOM)
883 gfx.anim_random_frame = RND(group->num_elements_resolved);
885 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
886 group->choice_mode, 0,
889 if (group->choice_mode == ANIM_RANDOM)
890 gfx.anim_random_frame = last_anim_random_frame;
894 element = group->element_resolved[element_pos];
900 static void InitPlayerField(int x, int y, int element, boolean init_game)
902 if (element == EL_SP_MURPHY)
906 if (stored_player[0].present)
908 Feld[x][y] = EL_SP_MURPHY_CLONE;
914 stored_player[0].use_murphy = TRUE;
916 if (!level.use_artwork_element[0])
917 stored_player[0].artwork_element = EL_SP_MURPHY;
920 Feld[x][y] = EL_PLAYER_1;
926 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
927 int jx = player->jx, jy = player->jy;
929 player->present = TRUE;
931 player->block_last_field = (element == EL_SP_MURPHY ?
932 level.sp_block_last_field :
933 level.block_last_field);
935 /* ---------- initialize player's last field block delay --------------- */
937 /* always start with reliable default value (no adjustment needed) */
938 player->block_delay_adjustment = 0;
940 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
941 if (player->block_last_field && element == EL_SP_MURPHY)
942 player->block_delay_adjustment = 1;
944 /* special case 2: in game engines before 3.1.1, blocking was different */
945 if (game.use_block_last_field_bug)
946 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
948 if (!options.network || player->connected)
950 player->active = TRUE;
952 /* remove potentially duplicate players */
953 if (StorePlayer[jx][jy] == Feld[x][y])
954 StorePlayer[jx][jy] = 0;
956 StorePlayer[x][y] = Feld[x][y];
960 printf("Player %d activated.\n", player->element_nr);
961 printf("[Local player is %d and currently %s.]\n",
962 local_player->element_nr,
963 local_player->active ? "active" : "not active");
967 Feld[x][y] = EL_EMPTY;
969 player->jx = player->last_jx = x;
970 player->jy = player->last_jy = y;
974 static void InitField(int x, int y, boolean init_game)
976 int element = Feld[x][y];
985 InitPlayerField(x, y, element, init_game);
988 case EL_SOKOBAN_FIELD_PLAYER:
989 element = Feld[x][y] = EL_PLAYER_1;
990 InitField(x, y, init_game);
992 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
993 InitField(x, y, init_game);
996 case EL_SOKOBAN_FIELD_EMPTY:
997 local_player->sokobanfields_still_needed++;
1001 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1002 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1003 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1004 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1005 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1006 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1007 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1008 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1009 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1010 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1019 case EL_SPACESHIP_RIGHT:
1020 case EL_SPACESHIP_UP:
1021 case EL_SPACESHIP_LEFT:
1022 case EL_SPACESHIP_DOWN:
1023 case EL_BD_BUTTERFLY:
1024 case EL_BD_BUTTERFLY_RIGHT:
1025 case EL_BD_BUTTERFLY_UP:
1026 case EL_BD_BUTTERFLY_LEFT:
1027 case EL_BD_BUTTERFLY_DOWN:
1029 case EL_BD_FIREFLY_RIGHT:
1030 case EL_BD_FIREFLY_UP:
1031 case EL_BD_FIREFLY_LEFT:
1032 case EL_BD_FIREFLY_DOWN:
1033 case EL_PACMAN_RIGHT:
1035 case EL_PACMAN_LEFT:
1036 case EL_PACMAN_DOWN:
1038 case EL_YAMYAM_LEFT:
1039 case EL_YAMYAM_RIGHT:
1041 case EL_YAMYAM_DOWN:
1042 case EL_DARK_YAMYAM:
1045 case EL_SP_SNIKSNAK:
1046 case EL_SP_ELECTRON:
1055 case EL_AMOEBA_FULL:
1060 case EL_AMOEBA_DROP:
1061 if (y == lev_fieldy - 1)
1063 Feld[x][y] = EL_AMOEBA_GROWING;
1064 Store[x][y] = EL_AMOEBA_WET;
1068 case EL_DYNAMITE_ACTIVE:
1069 case EL_SP_DISK_RED_ACTIVE:
1070 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1071 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1072 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1073 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1074 MovDelay[x][y] = 96;
1077 case EL_EM_DYNAMITE_ACTIVE:
1078 MovDelay[x][y] = 32;
1082 local_player->lights_still_needed++;
1086 local_player->friends_still_needed++;
1091 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1094 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1095 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1096 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1097 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1098 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1099 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1100 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1101 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1102 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1103 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1104 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1105 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1108 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1109 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1110 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1112 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1114 game.belt_dir[belt_nr] = belt_dir;
1115 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1117 else /* more than one switch -- set it like the first switch */
1119 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1124 #if !USE_BOTH_SWITCHGATE_SWITCHES
1125 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1127 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1131 case EL_LIGHT_SWITCH_ACTIVE:
1133 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1136 case EL_INVISIBLE_STEELWALL:
1137 case EL_INVISIBLE_WALL:
1138 case EL_INVISIBLE_SAND:
1139 if (game.light_time_left > 0 ||
1140 game.lenses_time_left > 0)
1141 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1144 case EL_EMC_MAGIC_BALL:
1145 if (game.ball_state)
1146 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1149 case EL_EMC_MAGIC_BALL_SWITCH:
1150 if (game.ball_state)
1151 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1155 if (IS_CUSTOM_ELEMENT(element))
1157 if (CAN_MOVE(element))
1160 #if USE_NEW_CUSTOM_VALUE
1161 if (!element_info[element].use_last_ce_value || init_game)
1162 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1165 else if (IS_GROUP_ELEMENT(element))
1167 Feld[x][y] = get_element_from_group_element(element);
1169 InitField(x, y, init_game);
1176 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1179 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1181 InitField(x, y, init_game);
1183 /* not needed to call InitMovDir() -- already done by InitField()! */
1184 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1185 CAN_MOVE(Feld[x][y]))
1189 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1191 int old_element = Feld[x][y];
1193 InitField(x, y, init_game);
1195 /* not needed to call InitMovDir() -- already done by InitField()! */
1196 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1197 CAN_MOVE(old_element) &&
1198 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1201 /* this case is in fact a combination of not less than three bugs:
1202 first, it calls InitMovDir() for elements that can move, although this is
1203 already done by InitField(); then, it checks the element that was at this
1204 field _before_ the call to InitField() (which can change it); lastly, it
1205 was not called for "mole with direction" elements, which were treated as
1206 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1210 inline void DrawGameValue_Emeralds(int value)
1212 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1214 if (PANEL_DEACTIVATED(game.panel.gems))
1217 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1220 inline void DrawGameValue_Dynamite(int value)
1222 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1224 if (PANEL_DEACTIVATED(game.panel.inventory))
1227 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1230 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1232 int base_key_graphic = EL_KEY_1;
1235 if (PANEL_DEACTIVATED(game.panel.keys))
1238 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1239 base_key_graphic = EL_EM_KEY_1;
1241 /* currently only 4 of 8 possible keys are displayed */
1242 for (i = 0; i < STD_NUM_KEYS; i++)
1244 int x = XX_KEYS + i * MINI_TILEX;
1248 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1250 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1251 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1255 inline void DrawGameValue_Score(int value)
1257 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1259 if (PANEL_DEACTIVATED(game.panel.score))
1262 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1265 inline void DrawGameValue_Time(int value)
1267 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1268 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1270 if (PANEL_DEACTIVATED(game.panel.time))
1273 /* clear background if value just changed its size */
1274 if (value == 999 || value == 1000)
1275 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1278 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1280 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1283 inline void DrawGameValue_Level(int value)
1285 if (PANEL_DEACTIVATED(game.panel.level))
1289 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1291 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1294 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1297 int key[MAX_NUM_KEYS];
1300 /* prevent EM engine from updating time/score values parallel to GameWon() */
1301 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1302 local_player->LevelSolved)
1305 for (i = 0; i < MAX_NUM_KEYS; i++)
1306 key[i] = key_bits & (1 << i);
1308 DrawGameValue_Level(level_nr);
1310 DrawGameValue_Emeralds(emeralds);
1311 DrawGameValue_Dynamite(dynamite);
1312 DrawGameValue_Score(score);
1313 DrawGameValue_Time(time);
1315 DrawGameValue_Keys(key);
1318 void DrawGameDoorValues()
1320 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1321 int dynamite_value = 0;
1322 int score_value = (local_player->LevelSolved ? local_player->score_final :
1323 local_player->score);
1324 int gems_value = local_player->gems_still_needed;
1328 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1330 DrawGameDoorValues_EM();
1335 if (game.centered_player_nr == -1)
1337 for (i = 0; i < MAX_PLAYERS; i++)
1339 for (j = 0; j < MAX_NUM_KEYS; j++)
1340 if (stored_player[i].key[j])
1341 key_bits |= (1 << j);
1343 dynamite_value += stored_player[i].inventory_size;
1348 int player_nr = game.centered_player_nr;
1350 for (i = 0; i < MAX_NUM_KEYS; i++)
1351 if (stored_player[player_nr].key[i])
1352 key_bits |= (1 << i);
1354 dynamite_value = stored_player[player_nr].inventory_size;
1357 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1363 =============================================================================
1365 -----------------------------------------------------------------------------
1366 initialize game engine due to level / tape version number
1367 =============================================================================
1370 static void InitGameEngine()
1372 int i, j, k, l, x, y;
1374 /* set game engine from tape file when re-playing, else from level file */
1375 game.engine_version = (tape.playing ? tape.engine_version :
1376 level.game_version);
1378 /* ---------------------------------------------------------------------- */
1379 /* set flags for bugs and changes according to active game engine version */
1380 /* ---------------------------------------------------------------------- */
1383 Summary of bugfix/change:
1384 Fixed handling for custom elements that change when pushed by the player.
1386 Fixed/changed in version:
1390 Before 3.1.0, custom elements that "change when pushing" changed directly
1391 after the player started pushing them (until then handled in "DigField()").
1392 Since 3.1.0, these custom elements are not changed until the "pushing"
1393 move of the element is finished (now handled in "ContinueMoving()").
1395 Affected levels/tapes:
1396 The first condition is generally needed for all levels/tapes before version
1397 3.1.0, which might use the old behaviour before it was changed; known tapes
1398 that are affected are some tapes from the level set "Walpurgis Gardens" by
1400 The second condition is an exception from the above case and is needed for
1401 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1402 above (including some development versions of 3.1.0), but before it was
1403 known that this change would break tapes like the above and was fixed in
1404 3.1.1, so that the changed behaviour was active although the engine version
1405 while recording maybe was before 3.1.0. There is at least one tape that is
1406 affected by this exception, which is the tape for the one-level set "Bug
1407 Machine" by Juergen Bonhagen.
1410 game.use_change_when_pushing_bug =
1411 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1413 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1414 tape.game_version < VERSION_IDENT(3,1,1,0)));
1417 Summary of bugfix/change:
1418 Fixed handling for blocking the field the player leaves when moving.
1420 Fixed/changed in version:
1424 Before 3.1.1, when "block last field when moving" was enabled, the field
1425 the player is leaving when moving was blocked for the time of the move,
1426 and was directly unblocked afterwards. This resulted in the last field
1427 being blocked for exactly one less than the number of frames of one player
1428 move. Additionally, even when blocking was disabled, the last field was
1429 blocked for exactly one frame.
1430 Since 3.1.1, due to changes in player movement handling, the last field
1431 is not blocked at all when blocking is disabled. When blocking is enabled,
1432 the last field is blocked for exactly the number of frames of one player
1433 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1434 last field is blocked for exactly one more than the number of frames of
1437 Affected levels/tapes:
1438 (!!! yet to be determined -- probably many !!!)
1441 game.use_block_last_field_bug =
1442 (game.engine_version < VERSION_IDENT(3,1,1,0));
1445 Summary of bugfix/change:
1446 Changed behaviour of CE changes with multiple changes per single frame.
1448 Fixed/changed in version:
1452 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1453 This resulted in race conditions where CEs seem to behave strange in some
1454 situations (where triggered CE changes were just skipped because there was
1455 already a CE change on that tile in the playfield in that engine frame).
1456 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1457 (The number of changes per frame must be limited in any case, because else
1458 it is easily possible to define CE changes that would result in an infinite
1459 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1460 should be set large enough so that it would only be reached in cases where
1461 the corresponding CE change conditions run into a loop. Therefore, it seems
1462 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1463 maximal number of change pages for custom elements.)
1465 Affected levels/tapes:
1469 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1470 game.max_num_changes_per_frame = 1;
1472 game.max_num_changes_per_frame =
1473 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1476 /* ---------------------------------------------------------------------- */
1478 /* default scan direction: scan playfield from top/left to bottom/right */
1479 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1481 /* dynamically adjust element properties according to game engine version */
1482 InitElementPropertiesEngine(game.engine_version);
1485 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1486 printf(" tape version == %06d [%s] [file: %06d]\n",
1487 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1489 printf(" => game.engine_version == %06d\n", game.engine_version);
1492 /* ---------- initialize player's initial move delay --------------------- */
1494 /* dynamically adjust player properties according to level information */
1495 for (i = 0; i < MAX_PLAYERS; i++)
1496 game.initial_move_delay_value[i] =
1497 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1499 /* dynamically adjust player properties according to game engine version */
1500 for (i = 0; i < MAX_PLAYERS; i++)
1501 game.initial_move_delay[i] =
1502 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1503 game.initial_move_delay_value[i] : 0);
1505 /* ---------- initialize player's initial push delay --------------------- */
1507 /* dynamically adjust player properties according to game engine version */
1508 game.initial_push_delay_value =
1509 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1511 /* ---------- initialize changing elements ------------------------------- */
1513 /* initialize changing elements information */
1514 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1516 struct ElementInfo *ei = &element_info[i];
1518 /* this pointer might have been changed in the level editor */
1519 ei->change = &ei->change_page[0];
1521 if (!IS_CUSTOM_ELEMENT(i))
1523 ei->change->target_element = EL_EMPTY_SPACE;
1524 ei->change->delay_fixed = 0;
1525 ei->change->delay_random = 0;
1526 ei->change->delay_frames = 1;
1529 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1531 ei->has_change_event[j] = FALSE;
1533 ei->event_page_nr[j] = 0;
1534 ei->event_page[j] = &ei->change_page[0];
1538 /* add changing elements from pre-defined list */
1539 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1541 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1542 struct ElementInfo *ei = &element_info[ch_delay->element];
1544 ei->change->target_element = ch_delay->target_element;
1545 ei->change->delay_fixed = ch_delay->change_delay;
1547 ei->change->pre_change_function = ch_delay->pre_change_function;
1548 ei->change->change_function = ch_delay->change_function;
1549 ei->change->post_change_function = ch_delay->post_change_function;
1551 ei->change->can_change = TRUE;
1552 ei->change->can_change_or_has_action = TRUE;
1554 ei->has_change_event[CE_DELAY] = TRUE;
1556 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1557 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1560 /* ---------- initialize internal run-time variables ------------- */
1562 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1564 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1566 for (j = 0; j < ei->num_change_pages; j++)
1568 ei->change_page[j].can_change_or_has_action =
1569 (ei->change_page[j].can_change |
1570 ei->change_page[j].has_action);
1574 /* add change events from custom element configuration */
1575 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1577 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1579 for (j = 0; j < ei->num_change_pages; j++)
1581 if (!ei->change_page[j].can_change_or_has_action)
1584 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1586 /* only add event page for the first page found with this event */
1587 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1589 ei->has_change_event[k] = TRUE;
1591 ei->event_page_nr[k] = j;
1592 ei->event_page[k] = &ei->change_page[j];
1598 /* ---------- initialize run-time trigger player and element ------------- */
1600 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1602 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1604 for (j = 0; j < ei->num_change_pages; j++)
1606 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1607 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1608 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1609 ei->change_page[j].actual_trigger_ce_value = 0;
1610 ei->change_page[j].actual_trigger_ce_score = 0;
1614 /* ---------- initialize trigger events ---------------------------------- */
1616 /* initialize trigger events information */
1617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1618 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1619 trigger_events[i][j] = FALSE;
1621 /* add trigger events from element change event properties */
1622 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1624 struct ElementInfo *ei = &element_info[i];
1626 for (j = 0; j < ei->num_change_pages; j++)
1628 if (!ei->change_page[j].can_change_or_has_action)
1631 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1633 int trigger_element = ei->change_page[j].trigger_element;
1635 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1637 if (ei->change_page[j].has_event[k])
1639 if (IS_GROUP_ELEMENT(trigger_element))
1641 struct ElementGroupInfo *group =
1642 element_info[trigger_element].group;
1644 for (l = 0; l < group->num_elements_resolved; l++)
1645 trigger_events[group->element_resolved[l]][k] = TRUE;
1647 else if (trigger_element == EL_ANY_ELEMENT)
1648 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1649 trigger_events[l][k] = TRUE;
1651 trigger_events[trigger_element][k] = TRUE;
1658 /* ---------- initialize push delay -------------------------------------- */
1660 /* initialize push delay values to default */
1661 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1663 if (!IS_CUSTOM_ELEMENT(i))
1665 /* set default push delay values (corrected since version 3.0.7-1) */
1666 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1668 element_info[i].push_delay_fixed = 2;
1669 element_info[i].push_delay_random = 8;
1673 element_info[i].push_delay_fixed = 8;
1674 element_info[i].push_delay_random = 8;
1679 /* set push delay value for certain elements from pre-defined list */
1680 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1682 int e = push_delay_list[i].element;
1684 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1685 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1688 /* set push delay value for Supaplex elements for newer engine versions */
1689 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1691 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1693 if (IS_SP_ELEMENT(i))
1695 /* set SP push delay to just enough to push under a falling zonk */
1696 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1698 element_info[i].push_delay_fixed = delay;
1699 element_info[i].push_delay_random = 0;
1704 /* ---------- initialize move stepsize ----------------------------------- */
1706 /* initialize move stepsize values to default */
1707 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1708 if (!IS_CUSTOM_ELEMENT(i))
1709 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1711 /* set move stepsize value for certain elements from pre-defined list */
1712 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1714 int e = move_stepsize_list[i].element;
1716 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1719 /* ---------- initialize collect score ----------------------------------- */
1721 /* initialize collect score values for custom elements from initial value */
1722 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1723 if (IS_CUSTOM_ELEMENT(i))
1724 element_info[i].collect_score = element_info[i].collect_score_initial;
1726 /* ---------- initialize collect count ----------------------------------- */
1728 /* initialize collect count values for non-custom elements */
1729 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1730 if (!IS_CUSTOM_ELEMENT(i))
1731 element_info[i].collect_count_initial = 0;
1733 /* add collect count values for all elements from pre-defined list */
1734 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1735 element_info[collect_count_list[i].element].collect_count_initial =
1736 collect_count_list[i].count;
1738 /* ---------- initialize access direction -------------------------------- */
1740 /* initialize access direction values to default (access from every side) */
1741 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1742 if (!IS_CUSTOM_ELEMENT(i))
1743 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1745 /* set access direction value for certain elements from pre-defined list */
1746 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1747 element_info[access_direction_list[i].element].access_direction =
1748 access_direction_list[i].direction;
1750 /* ---------- initialize explosion content ------------------------------- */
1751 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1753 if (IS_CUSTOM_ELEMENT(i))
1756 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1758 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1760 element_info[i].content.e[x][y] =
1761 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1762 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1763 i == EL_PLAYER_3 ? EL_EMERALD :
1764 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1765 i == EL_MOLE ? EL_EMERALD_RED :
1766 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1767 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1768 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1769 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1770 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1771 i == EL_WALL_EMERALD ? EL_EMERALD :
1772 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1773 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1774 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1775 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1776 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1777 i == EL_WALL_PEARL ? EL_PEARL :
1778 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1784 int get_num_special_action(int element, int action_first, int action_last)
1786 int num_special_action = 0;
1789 for (i = action_first; i <= action_last; i++)
1791 boolean found = FALSE;
1793 for (j = 0; j < NUM_DIRECTIONS; j++)
1794 if (el_act_dir2img(element, i, j) !=
1795 el_act_dir2img(element, ACTION_DEFAULT, j))
1799 num_special_action++;
1804 return num_special_action;
1809 =============================================================================
1811 -----------------------------------------------------------------------------
1812 initialize and start new game
1813 =============================================================================
1818 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1819 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1820 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1821 boolean do_fading = (game_status == GAME_MODE_MAIN);
1824 game_status = GAME_MODE_PLAYING;
1828 /* don't play tapes over network */
1829 network_playing = (options.network && !tape.playing);
1831 for (i = 0; i < MAX_PLAYERS; i++)
1833 struct PlayerInfo *player = &stored_player[i];
1835 player->index_nr = i;
1836 player->index_bit = (1 << i);
1837 player->element_nr = EL_PLAYER_1 + i;
1839 player->present = FALSE;
1840 player->active = FALSE;
1843 player->effective_action = 0;
1844 player->programmed_action = 0;
1847 player->score_final = 0;
1849 player->gems_still_needed = level.gems_needed;
1850 player->sokobanfields_still_needed = 0;
1851 player->lights_still_needed = 0;
1852 player->friends_still_needed = 0;
1854 for (j = 0; j < MAX_NUM_KEYS; j++)
1855 player->key[j] = FALSE;
1857 player->dynabomb_count = 0;
1858 player->dynabomb_size = 1;
1859 player->dynabombs_left = 0;
1860 player->dynabomb_xl = FALSE;
1862 player->MovDir = MV_NONE;
1865 player->GfxDir = MV_NONE;
1866 player->GfxAction = ACTION_DEFAULT;
1868 player->StepFrame = 0;
1870 player->use_murphy = FALSE;
1871 player->artwork_element =
1872 (level.use_artwork_element[i] ? level.artwork_element[i] :
1873 player->element_nr);
1875 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1876 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1878 player->gravity = level.initial_player_gravity[i];
1880 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1882 player->actual_frame_counter = 0;
1884 player->step_counter = 0;
1886 player->last_move_dir = MV_NONE;
1888 player->is_active = FALSE;
1890 player->is_waiting = FALSE;
1891 player->is_moving = FALSE;
1892 player->is_auto_moving = FALSE;
1893 player->is_digging = FALSE;
1894 player->is_snapping = FALSE;
1895 player->is_collecting = FALSE;
1896 player->is_pushing = FALSE;
1897 player->is_switching = FALSE;
1898 player->is_dropping = FALSE;
1899 player->is_dropping_pressed = FALSE;
1901 player->is_bored = FALSE;
1902 player->is_sleeping = FALSE;
1904 player->frame_counter_bored = -1;
1905 player->frame_counter_sleeping = -1;
1907 player->anim_delay_counter = 0;
1908 player->post_delay_counter = 0;
1910 player->dir_waiting = MV_NONE;
1911 player->action_waiting = ACTION_DEFAULT;
1912 player->last_action_waiting = ACTION_DEFAULT;
1913 player->special_action_bored = ACTION_DEFAULT;
1914 player->special_action_sleeping = ACTION_DEFAULT;
1916 player->switch_x = -1;
1917 player->switch_y = -1;
1919 player->drop_x = -1;
1920 player->drop_y = -1;
1922 player->show_envelope = 0;
1924 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1926 player->push_delay = -1; /* initialized when pushing starts */
1927 player->push_delay_value = game.initial_push_delay_value;
1929 player->drop_delay = 0;
1930 player->drop_pressed_delay = 0;
1932 player->last_jx = player->last_jy = 0;
1933 player->jx = player->jy = 0;
1935 player->shield_normal_time_left = 0;
1936 player->shield_deadly_time_left = 0;
1938 player->inventory_infinite_element = EL_UNDEFINED;
1939 player->inventory_size = 0;
1941 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1942 SnapField(player, 0, 0);
1944 player->LevelSolved = FALSE;
1945 player->GameOver = FALSE;
1947 player->LevelSolved_GameEnd = FALSE;
1948 player->LevelSolved_SaveTape = FALSE;
1949 player->LevelSolved_SaveScore = FALSE;
1952 network_player_action_received = FALSE;
1954 #if defined(NETWORK_AVALIABLE)
1955 /* initial null action */
1956 if (network_playing)
1957 SendToServer_MovePlayer(MV_NONE);
1966 TimeLeft = level.time;
1969 ScreenMovDir = MV_NONE;
1973 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1975 AllPlayersGone = FALSE;
1977 game.yamyam_content_nr = 0;
1978 game.magic_wall_active = FALSE;
1979 game.magic_wall_time_left = 0;
1980 game.light_time_left = 0;
1981 game.timegate_time_left = 0;
1982 game.switchgate_pos = 0;
1983 game.wind_direction = level.wind_direction_initial;
1985 #if !USE_PLAYER_GRAVITY
1986 game.gravity = FALSE;
1987 game.explosions_delayed = TRUE;
1990 game.lenses_time_left = 0;
1991 game.magnify_time_left = 0;
1993 game.ball_state = level.ball_state_initial;
1994 game.ball_content_nr = 0;
1996 game.envelope_active = FALSE;
1998 /* set focus to local player for network games, else to all players */
1999 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2000 game.centered_player_nr_next = game.centered_player_nr;
2001 game.set_centered_player = FALSE;
2003 if (network_playing && tape.recording)
2005 /* store client dependent player focus when recording network games */
2006 tape.centered_player_nr_next = game.centered_player_nr_next;
2007 tape.set_centered_player = TRUE;
2010 for (i = 0; i < NUM_BELTS; i++)
2012 game.belt_dir[i] = MV_NONE;
2013 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2016 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2017 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2019 SCAN_PLAYFIELD(x, y)
2021 Feld[x][y] = level.field[x][y];
2022 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2023 ChangeDelay[x][y] = 0;
2024 ChangePage[x][y] = -1;
2025 #if USE_NEW_CUSTOM_VALUE
2026 CustomValue[x][y] = 0; /* initialized in InitField() */
2028 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2030 WasJustMoving[x][y] = 0;
2031 WasJustFalling[x][y] = 0;
2032 CheckCollision[x][y] = 0;
2034 Pushed[x][y] = FALSE;
2036 ChangeCount[x][y] = 0;
2037 ChangeEvent[x][y] = -1;
2039 ExplodePhase[x][y] = 0;
2040 ExplodeDelay[x][y] = 0;
2041 ExplodeField[x][y] = EX_TYPE_NONE;
2043 RunnerVisit[x][y] = 0;
2044 PlayerVisit[x][y] = 0;
2047 GfxRandom[x][y] = INIT_GFX_RANDOM();
2048 GfxElement[x][y] = EL_UNDEFINED;
2049 GfxAction[x][y] = ACTION_DEFAULT;
2050 GfxDir[x][y] = MV_NONE;
2053 SCAN_PLAYFIELD(x, y)
2055 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2057 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2059 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2062 InitField(x, y, TRUE);
2067 for (i = 0; i < MAX_PLAYERS; i++)
2069 struct PlayerInfo *player = &stored_player[i];
2071 /* set number of special actions for bored and sleeping animation */
2072 player->num_special_action_bored =
2073 get_num_special_action(player->artwork_element,
2074 ACTION_BORING_1, ACTION_BORING_LAST);
2075 player->num_special_action_sleeping =
2076 get_num_special_action(player->artwork_element,
2077 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2080 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2081 emulate_sb ? EMU_SOKOBAN :
2082 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2084 #if USE_NEW_ALL_SLIPPERY
2085 /* initialize type of slippery elements */
2086 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2088 if (!IS_CUSTOM_ELEMENT(i))
2090 /* default: elements slip down either to the left or right randomly */
2091 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2093 /* SP style elements prefer to slip down on the left side */
2094 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2095 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2097 /* BD style elements prefer to slip down on the left side */
2098 if (game.emulation == EMU_BOULDERDASH)
2099 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2104 /* initialize explosion and ignition delay */
2105 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2107 if (!IS_CUSTOM_ELEMENT(i))
2110 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2111 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2112 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2113 int last_phase = (num_phase + 1) * delay;
2114 int half_phase = (num_phase / 2) * delay;
2116 element_info[i].explosion_delay = last_phase - 1;
2117 element_info[i].ignition_delay = half_phase;
2119 if (i == EL_BLACK_ORB)
2120 element_info[i].ignition_delay = 1;
2124 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2125 element_info[i].explosion_delay = 1;
2127 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2128 element_info[i].ignition_delay = 1;
2132 /* correct non-moving belts to start moving left */
2133 for (i = 0; i < NUM_BELTS; i++)
2134 if (game.belt_dir[i] == MV_NONE)
2135 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2137 /* check if any connected player was not found in playfield */
2138 for (i = 0; i < MAX_PLAYERS; i++)
2140 struct PlayerInfo *player = &stored_player[i];
2142 if (player->connected && !player->present)
2144 for (j = 0; j < MAX_PLAYERS; j++)
2146 struct PlayerInfo *some_player = &stored_player[j];
2147 int jx = some_player->jx, jy = some_player->jy;
2149 /* assign first free player found that is present in the playfield */
2150 if (some_player->present && !some_player->connected)
2152 player->present = TRUE;
2153 player->active = TRUE;
2155 some_player->present = FALSE;
2156 some_player->active = FALSE;
2158 player->artwork_element = some_player->artwork_element;
2160 player->block_last_field = some_player->block_last_field;
2161 player->block_delay_adjustment = some_player->block_delay_adjustment;
2163 StorePlayer[jx][jy] = player->element_nr;
2164 player->jx = player->last_jx = jx;
2165 player->jy = player->last_jy = jy;
2175 /* when playing a tape, eliminate all players who do not participate */
2177 for (i = 0; i < MAX_PLAYERS; i++)
2179 if (stored_player[i].active && !tape.player_participates[i])
2181 struct PlayerInfo *player = &stored_player[i];
2182 int jx = player->jx, jy = player->jy;
2184 player->active = FALSE;
2185 StorePlayer[jx][jy] = 0;
2186 Feld[jx][jy] = EL_EMPTY;
2190 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2192 /* when in single player mode, eliminate all but the first active player */
2194 for (i = 0; i < MAX_PLAYERS; i++)
2196 if (stored_player[i].active)
2198 for (j = i + 1; j < MAX_PLAYERS; j++)
2200 if (stored_player[j].active)
2202 struct PlayerInfo *player = &stored_player[j];
2203 int jx = player->jx, jy = player->jy;
2205 player->active = FALSE;
2206 player->present = FALSE;
2208 StorePlayer[jx][jy] = 0;
2209 Feld[jx][jy] = EL_EMPTY;
2216 /* when recording the game, store which players take part in the game */
2219 for (i = 0; i < MAX_PLAYERS; i++)
2220 if (stored_player[i].active)
2221 tape.player_participates[i] = TRUE;
2226 for (i = 0; i < MAX_PLAYERS; i++)
2228 struct PlayerInfo *player = &stored_player[i];
2230 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2235 if (local_player == player)
2236 printf("Player %d is local player.\n", i+1);
2240 if (BorderElement == EL_EMPTY)
2243 SBX_Right = lev_fieldx - SCR_FIELDX;
2245 SBY_Lower = lev_fieldy - SCR_FIELDY;
2250 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2252 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2255 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2256 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2258 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2259 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2261 /* if local player not found, look for custom element that might create
2262 the player (make some assumptions about the right custom element) */
2263 if (!local_player->present)
2265 int start_x = 0, start_y = 0;
2266 int found_rating = 0;
2267 int found_element = EL_UNDEFINED;
2268 int player_nr = local_player->index_nr;
2270 SCAN_PLAYFIELD(x, y)
2272 int element = Feld[x][y];
2277 if (level.use_start_element[player_nr] &&
2278 level.start_element[player_nr] == element &&
2285 found_element = element;
2288 if (!IS_CUSTOM_ELEMENT(element))
2291 if (CAN_CHANGE(element))
2293 for (i = 0; i < element_info[element].num_change_pages; i++)
2295 /* check for player created from custom element as single target */
2296 content = element_info[element].change_page[i].target_element;
2297 is_player = ELEM_IS_PLAYER(content);
2299 if (is_player && (found_rating < 3 || element < found_element))
2305 found_element = element;
2310 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2312 /* check for player created from custom element as explosion content */
2313 content = element_info[element].content.e[xx][yy];
2314 is_player = ELEM_IS_PLAYER(content);
2316 if (is_player && (found_rating < 2 || element < found_element))
2318 start_x = x + xx - 1;
2319 start_y = y + yy - 1;
2322 found_element = element;
2325 if (!CAN_CHANGE(element))
2328 for (i = 0; i < element_info[element].num_change_pages; i++)
2330 /* check for player created from custom element as extended target */
2332 element_info[element].change_page[i].target_content.e[xx][yy];
2334 is_player = ELEM_IS_PLAYER(content);
2336 if (is_player && (found_rating < 1 || element < found_element))
2338 start_x = x + xx - 1;
2339 start_y = y + yy - 1;
2342 found_element = element;
2348 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2349 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2352 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2353 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2358 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2359 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2360 local_player->jx - MIDPOSX);
2362 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2363 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2364 local_player->jy - MIDPOSY);
2369 if (!game.restart_level)
2370 CloseDoor(DOOR_CLOSE_1);
2373 FadeOut(REDRAW_FIELD);
2375 /* !!! FIX THIS (START) !!! */
2376 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2378 InitGameEngine_EM();
2380 /* blit playfield from scroll buffer to normal back buffer for fading in */
2381 BlitScreenToBitmap_EM(backbuffer);
2388 /* after drawing the level, correct some elements */
2389 if (game.timegate_time_left == 0)
2390 CloseAllOpenTimegates();
2392 /* blit playfield from scroll buffer to normal back buffer for fading in */
2393 if (setup.soft_scrolling)
2394 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2396 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2398 /* !!! FIX THIS (END) !!! */
2401 FadeIn(REDRAW_FIELD);
2405 if (!game.restart_level)
2407 /* copy default game door content to main double buffer */
2408 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2409 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2412 SetPanelBackground();
2413 SetDrawBackgroundMask(REDRAW_DOOR_1);
2415 DrawGameDoorValues();
2417 if (!game.restart_level)
2421 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2422 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2423 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2427 /* copy actual game door content to door double buffer for OpenDoor() */
2428 BlitBitmap(drawto, bitmap_db_door,
2429 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2431 OpenDoor(DOOR_OPEN_ALL);
2433 PlaySound(SND_GAME_STARTING);
2435 if (setup.sound_music)
2438 KeyboardAutoRepeatOffUnlessAutoplay();
2442 for (i = 0; i < MAX_PLAYERS; i++)
2443 printf("Player %d %sactive.\n",
2444 i + 1, (stored_player[i].active ? "" : "not "));
2455 game.restart_level = FALSE;
2458 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2460 /* this is used for non-R'n'D game engines to update certain engine values */
2462 /* needed to determine if sounds are played within the visible screen area */
2463 scroll_x = actual_scroll_x;
2464 scroll_y = actual_scroll_y;
2467 void InitMovDir(int x, int y)
2469 int i, element = Feld[x][y];
2470 static int xy[4][2] =
2477 static int direction[3][4] =
2479 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2480 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2481 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2490 Feld[x][y] = EL_BUG;
2491 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2494 case EL_SPACESHIP_RIGHT:
2495 case EL_SPACESHIP_UP:
2496 case EL_SPACESHIP_LEFT:
2497 case EL_SPACESHIP_DOWN:
2498 Feld[x][y] = EL_SPACESHIP;
2499 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2502 case EL_BD_BUTTERFLY_RIGHT:
2503 case EL_BD_BUTTERFLY_UP:
2504 case EL_BD_BUTTERFLY_LEFT:
2505 case EL_BD_BUTTERFLY_DOWN:
2506 Feld[x][y] = EL_BD_BUTTERFLY;
2507 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2510 case EL_BD_FIREFLY_RIGHT:
2511 case EL_BD_FIREFLY_UP:
2512 case EL_BD_FIREFLY_LEFT:
2513 case EL_BD_FIREFLY_DOWN:
2514 Feld[x][y] = EL_BD_FIREFLY;
2515 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2518 case EL_PACMAN_RIGHT:
2520 case EL_PACMAN_LEFT:
2521 case EL_PACMAN_DOWN:
2522 Feld[x][y] = EL_PACMAN;
2523 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2526 case EL_YAMYAM_LEFT:
2527 case EL_YAMYAM_RIGHT:
2529 case EL_YAMYAM_DOWN:
2530 Feld[x][y] = EL_YAMYAM;
2531 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2534 case EL_SP_SNIKSNAK:
2535 MovDir[x][y] = MV_UP;
2538 case EL_SP_ELECTRON:
2539 MovDir[x][y] = MV_LEFT;
2546 Feld[x][y] = EL_MOLE;
2547 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2551 if (IS_CUSTOM_ELEMENT(element))
2553 struct ElementInfo *ei = &element_info[element];
2554 int move_direction_initial = ei->move_direction_initial;
2555 int move_pattern = ei->move_pattern;
2557 if (move_direction_initial == MV_START_PREVIOUS)
2559 if (MovDir[x][y] != MV_NONE)
2562 move_direction_initial = MV_START_AUTOMATIC;
2565 if (move_direction_initial == MV_START_RANDOM)
2566 MovDir[x][y] = 1 << RND(4);
2567 else if (move_direction_initial & MV_ANY_DIRECTION)
2568 MovDir[x][y] = move_direction_initial;
2569 else if (move_pattern == MV_ALL_DIRECTIONS ||
2570 move_pattern == MV_TURNING_LEFT ||
2571 move_pattern == MV_TURNING_RIGHT ||
2572 move_pattern == MV_TURNING_LEFT_RIGHT ||
2573 move_pattern == MV_TURNING_RIGHT_LEFT ||
2574 move_pattern == MV_TURNING_RANDOM)
2575 MovDir[x][y] = 1 << RND(4);
2576 else if (move_pattern == MV_HORIZONTAL)
2577 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2578 else if (move_pattern == MV_VERTICAL)
2579 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2580 else if (move_pattern & MV_ANY_DIRECTION)
2581 MovDir[x][y] = element_info[element].move_pattern;
2582 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2583 move_pattern == MV_ALONG_RIGHT_SIDE)
2585 /* use random direction as default start direction */
2586 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2587 MovDir[x][y] = 1 << RND(4);
2589 for (i = 0; i < NUM_DIRECTIONS; i++)
2591 int x1 = x + xy[i][0];
2592 int y1 = y + xy[i][1];
2594 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2596 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2597 MovDir[x][y] = direction[0][i];
2599 MovDir[x][y] = direction[1][i];
2608 MovDir[x][y] = 1 << RND(4);
2610 if (element != EL_BUG &&
2611 element != EL_SPACESHIP &&
2612 element != EL_BD_BUTTERFLY &&
2613 element != EL_BD_FIREFLY)
2616 for (i = 0; i < NUM_DIRECTIONS; i++)
2618 int x1 = x + xy[i][0];
2619 int y1 = y + xy[i][1];
2621 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2623 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2625 MovDir[x][y] = direction[0][i];
2628 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2629 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2631 MovDir[x][y] = direction[1][i];
2640 GfxDir[x][y] = MovDir[x][y];
2643 void InitAmoebaNr(int x, int y)
2646 int group_nr = AmoebeNachbarNr(x, y);
2650 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2652 if (AmoebaCnt[i] == 0)
2660 AmoebaNr[x][y] = group_nr;
2661 AmoebaCnt[group_nr]++;
2662 AmoebaCnt2[group_nr]++;
2665 static void PlayerWins(struct PlayerInfo *player)
2667 player->LevelSolved = TRUE;
2668 player->GameOver = TRUE;
2670 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2671 level.native_em_level->lev->score : player->score);
2676 static int time, time_final;
2677 static int score, score_final;
2678 static int game_over_delay = 0;
2679 int game_over_delay_value = 50;
2681 if (!local_player->LevelSolved_GameEnd)
2685 /* do not start end game actions before the player stops moving (to exit) */
2686 if (local_player->MovPos)
2689 local_player->LevelSolved_GameEnd = TRUE;
2690 local_player->LevelSolved_SaveTape = tape.recording;
2691 local_player->LevelSolved_SaveScore = !tape.playing;
2693 if (tape.auto_play) /* tape might already be stopped here */
2694 tape.auto_play_level_solved = TRUE;
2700 game_over_delay = game_over_delay_value;
2702 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2703 score = score_final = local_player->score_final;
2708 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2710 else if (level.time == 0 && TimePlayed < 999)
2713 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2716 local_player->score_final = score_final;
2718 if (level_editor_test_game)
2721 score = score_final;
2723 DrawGameValue_Time(time);
2724 DrawGameValue_Score(score);
2727 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2729 /* close exit door after last player */
2730 if (AllPlayersGone &&
2731 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2732 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2734 int element = Feld[ExitX][ExitY];
2736 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2737 EL_SP_EXIT_CLOSING);
2739 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2742 /* player disappears */
2743 DrawLevelField(ExitX, ExitY);
2746 for (i = 0; i < MAX_PLAYERS; i++)
2748 struct PlayerInfo *player = &stored_player[i];
2750 if (player->present)
2752 RemovePlayer(player);
2754 /* player disappears */
2755 DrawLevelField(player->jx, player->jy);
2759 PlaySound(SND_GAME_WINNING);
2762 if (game_over_delay > 0)
2769 if (time != time_final)
2771 int time_to_go = ABS(time_final - time);
2772 int time_count_dir = (time < time_final ? +1 : -1);
2773 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2775 time += time_count_steps * time_count_dir;
2776 score += time_count_steps * level.score[SC_TIME_BONUS];
2778 DrawGameValue_Time(time);
2779 DrawGameValue_Score(score);
2781 if (time == time_final)
2782 StopSound(SND_GAME_LEVELTIME_BONUS);
2783 else if (setup.sound_loops)
2784 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2786 PlaySound(SND_GAME_LEVELTIME_BONUS);
2793 boolean raise_level = FALSE;
2795 CloseDoor(DOOR_CLOSE_1);
2797 if (local_player->LevelSolved_SaveTape)
2804 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2806 SaveTape(tape.level_nr); /* ask to save tape */
2810 if (level_editor_test_game)
2812 game_status = GAME_MODE_MAIN;
2819 if (!local_player->LevelSolved_SaveScore)
2821 FadeOut(REDRAW_FIELD);
2823 game_status = GAME_MODE_MAIN;
2825 DrawAndFadeInMainMenu(REDRAW_FIELD);
2830 if (level_nr == leveldir_current->handicap_level)
2832 leveldir_current->handicap_level++;
2833 SaveLevelSetup_SeriesInfo();
2836 if (level_nr < leveldir_current->last_level)
2837 raise_level = TRUE; /* advance to next level */
2839 if ((hi_pos = NewHiScore()) >= 0)
2841 game_status = GAME_MODE_SCORES;
2843 DrawHallOfFame(hi_pos);
2853 FadeOut(REDRAW_FIELD);
2855 game_status = GAME_MODE_MAIN;
2863 DrawAndFadeInMainMenu(REDRAW_FIELD);
2872 LoadScore(level_nr);
2874 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2875 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2878 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2880 if (local_player->score_final > highscore[k].Score)
2882 /* player has made it to the hall of fame */
2884 if (k < MAX_SCORE_ENTRIES - 1)
2886 int m = MAX_SCORE_ENTRIES - 1;
2889 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2890 if (strEqual(setup.player_name, highscore[l].Name))
2892 if (m == k) /* player's new highscore overwrites his old one */
2896 for (l = m; l > k; l--)
2898 strcpy(highscore[l].Name, highscore[l - 1].Name);
2899 highscore[l].Score = highscore[l - 1].Score;
2906 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2907 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2908 highscore[k].Score = local_player->score_final;
2914 else if (!strncmp(setup.player_name, highscore[k].Name,
2915 MAX_PLAYER_NAME_LEN))
2916 break; /* player already there with a higher score */
2922 SaveScore(level_nr);
2927 inline static int getElementMoveStepsize(int x, int y)
2929 int element = Feld[x][y];
2930 int direction = MovDir[x][y];
2931 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2932 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2933 int horiz_move = (dx != 0);
2934 int sign = (horiz_move ? dx : dy);
2935 int step = sign * element_info[element].move_stepsize;
2937 /* special values for move stepsize for spring and things on conveyor belt */
2940 if (CAN_FALL(element) &&
2941 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2942 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2943 else if (element == EL_SPRING)
2944 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2950 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2952 if (player->GfxAction != action || player->GfxDir != dir)
2955 printf("Player frame reset! (%d => %d, %d => %d)\n",
2956 player->GfxAction, action, player->GfxDir, dir);
2959 player->GfxAction = action;
2960 player->GfxDir = dir;
2962 player->StepFrame = 0;
2966 #if USE_GFX_RESET_GFX_ANIMATION
2967 static void ResetGfxFrame(int x, int y, boolean redraw)
2969 int element = Feld[x][y];
2970 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2971 int last_gfx_frame = GfxFrame[x][y];
2973 if (graphic_info[graphic].anim_global_sync)
2974 GfxFrame[x][y] = FrameCounter;
2975 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2976 GfxFrame[x][y] = CustomValue[x][y];
2977 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2978 GfxFrame[x][y] = element_info[element].collect_score;
2979 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2980 GfxFrame[x][y] = ChangeDelay[x][y];
2982 if (redraw && GfxFrame[x][y] != last_gfx_frame)
2983 DrawLevelGraphicAnimation(x, y, graphic);
2987 static void ResetGfxAnimation(int x, int y)
2989 GfxAction[x][y] = ACTION_DEFAULT;
2990 GfxDir[x][y] = MovDir[x][y];
2993 #if USE_GFX_RESET_GFX_ANIMATION
2994 ResetGfxFrame(x, y, FALSE);
2998 static void ResetRandomAnimationValue(int x, int y)
3000 GfxRandom[x][y] = INIT_GFX_RANDOM();
3003 void InitMovingField(int x, int y, int direction)
3005 int element = Feld[x][y];
3006 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3007 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3011 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3012 ResetGfxAnimation(x, y);
3014 MovDir[x][y] = direction;
3015 GfxDir[x][y] = direction;
3016 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3017 ACTION_FALLING : ACTION_MOVING);
3019 /* this is needed for CEs with property "can move" / "not moving" */
3021 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3023 if (Feld[newx][newy] == EL_EMPTY)
3024 Feld[newx][newy] = EL_BLOCKED;
3026 MovDir[newx][newy] = MovDir[x][y];
3028 #if USE_NEW_CUSTOM_VALUE
3029 CustomValue[newx][newy] = CustomValue[x][y];
3032 GfxFrame[newx][newy] = GfxFrame[x][y];
3033 GfxRandom[newx][newy] = GfxRandom[x][y];
3034 GfxAction[newx][newy] = GfxAction[x][y];
3035 GfxDir[newx][newy] = GfxDir[x][y];
3039 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3041 int direction = MovDir[x][y];
3042 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3043 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3049 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3051 int oldx = x, oldy = y;
3052 int direction = MovDir[x][y];
3054 if (direction == MV_LEFT)
3056 else if (direction == MV_RIGHT)
3058 else if (direction == MV_UP)
3060 else if (direction == MV_DOWN)
3063 *comes_from_x = oldx;
3064 *comes_from_y = oldy;
3067 int MovingOrBlocked2Element(int x, int y)
3069 int element = Feld[x][y];
3071 if (element == EL_BLOCKED)
3075 Blocked2Moving(x, y, &oldx, &oldy);
3076 return Feld[oldx][oldy];
3082 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3084 /* like MovingOrBlocked2Element(), but if element is moving
3085 and (x,y) is the field the moving element is just leaving,
3086 return EL_BLOCKED instead of the element value */
3087 int element = Feld[x][y];
3089 if (IS_MOVING(x, y))
3091 if (element == EL_BLOCKED)
3095 Blocked2Moving(x, y, &oldx, &oldy);
3096 return Feld[oldx][oldy];
3105 static void RemoveField(int x, int y)
3107 Feld[x][y] = EL_EMPTY;
3113 #if USE_NEW_CUSTOM_VALUE
3114 CustomValue[x][y] = 0;
3118 ChangeDelay[x][y] = 0;
3119 ChangePage[x][y] = -1;
3120 Pushed[x][y] = FALSE;
3123 ExplodeField[x][y] = EX_TYPE_NONE;
3126 GfxElement[x][y] = EL_UNDEFINED;
3127 GfxAction[x][y] = ACTION_DEFAULT;
3128 GfxDir[x][y] = MV_NONE;
3131 void RemoveMovingField(int x, int y)
3133 int oldx = x, oldy = y, newx = x, newy = y;
3134 int element = Feld[x][y];
3135 int next_element = EL_UNDEFINED;
3137 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3140 if (IS_MOVING(x, y))
3142 Moving2Blocked(x, y, &newx, &newy);
3144 if (Feld[newx][newy] != EL_BLOCKED)
3146 /* element is moving, but target field is not free (blocked), but
3147 already occupied by something different (example: acid pool);
3148 in this case, only remove the moving field, but not the target */
3150 RemoveField(oldx, oldy);
3152 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3154 DrawLevelField(oldx, oldy);
3159 else if (element == EL_BLOCKED)
3161 Blocked2Moving(x, y, &oldx, &oldy);
3162 if (!IS_MOVING(oldx, oldy))
3166 if (element == EL_BLOCKED &&
3167 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3168 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3169 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3170 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3171 next_element = get_next_element(Feld[oldx][oldy]);
3173 RemoveField(oldx, oldy);
3174 RemoveField(newx, newy);
3176 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3178 if (next_element != EL_UNDEFINED)
3179 Feld[oldx][oldy] = next_element;
3181 DrawLevelField(oldx, oldy);
3182 DrawLevelField(newx, newy);
3185 void DrawDynamite(int x, int y)
3187 int sx = SCREENX(x), sy = SCREENY(y);
3188 int graphic = el2img(Feld[x][y]);
3191 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3194 if (IS_WALKABLE_INSIDE(Back[x][y]))
3198 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3199 else if (Store[x][y])
3200 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3202 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3204 if (Back[x][y] || Store[x][y])
3205 DrawGraphicThruMask(sx, sy, graphic, frame);
3207 DrawGraphic(sx, sy, graphic, frame);
3210 void CheckDynamite(int x, int y)
3212 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3216 if (MovDelay[x][y] != 0)
3219 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3225 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3230 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3232 boolean num_checked_players = 0;
3235 for (i = 0; i < MAX_PLAYERS; i++)
3237 if (stored_player[i].active)
3239 int sx = stored_player[i].jx;
3240 int sy = stored_player[i].jy;
3242 if (num_checked_players == 0)
3249 *sx1 = MIN(*sx1, sx);
3250 *sy1 = MIN(*sy1, sy);
3251 *sx2 = MAX(*sx2, sx);
3252 *sy2 = MAX(*sy2, sy);
3255 num_checked_players++;
3260 static boolean checkIfAllPlayersFitToScreen_RND()
3262 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3264 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3266 return (sx2 - sx1 < SCR_FIELDX &&
3267 sy2 - sy1 < SCR_FIELDY);
3270 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3272 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3274 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3276 *sx = (sx1 + sx2) / 2;
3277 *sy = (sy1 + sy2) / 2;
3280 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3281 boolean quick_relocation)
3283 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3284 boolean no_delay = (tape.warp_forward);
3285 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3286 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3288 if (quick_relocation)
3290 int offset = (setup.scroll_delay ? 3 : 0);
3292 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3294 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3295 x > SBX_Right + MIDPOSX ? SBX_Right :
3298 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3299 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3304 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3305 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3306 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3308 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3309 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3310 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3312 /* don't scroll over playfield boundaries */
3313 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3314 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3316 /* don't scroll over playfield boundaries */
3317 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3318 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3321 RedrawPlayfield(TRUE, 0,0,0,0);
3325 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3326 x > SBX_Right + MIDPOSX ? SBX_Right :
3329 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3330 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3333 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3335 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3338 int fx = FX, fy = FY;
3340 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3341 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3343 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3349 fx += dx * TILEX / 2;
3350 fy += dy * TILEY / 2;
3352 ScrollLevel(dx, dy);
3355 /* scroll in two steps of half tile size to make things smoother */
3356 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3358 Delay(wait_delay_value);
3360 /* scroll second step to align at full tile size */
3362 Delay(wait_delay_value);
3367 Delay(wait_delay_value);
3371 void RelocatePlayer(int jx, int jy, int el_player_raw)
3373 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3374 int player_nr = GET_PLAYER_NR(el_player);
3375 struct PlayerInfo *player = &stored_player[player_nr];
3376 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3377 boolean no_delay = (tape.warp_forward);
3378 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3379 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3380 int old_jx = player->jx;
3381 int old_jy = player->jy;
3382 int old_element = Feld[old_jx][old_jy];
3383 int element = Feld[jx][jy];
3384 boolean player_relocated = (old_jx != jx || old_jy != jy);
3386 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3387 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3388 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3389 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3390 int leave_side_horiz = move_dir_horiz;
3391 int leave_side_vert = move_dir_vert;
3392 int enter_side = enter_side_horiz | enter_side_vert;
3393 int leave_side = leave_side_horiz | leave_side_vert;
3395 if (player->GameOver) /* do not reanimate dead player */
3398 if (!player_relocated) /* no need to relocate the player */
3401 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3403 RemoveField(jx, jy); /* temporarily remove newly placed player */
3404 DrawLevelField(jx, jy);
3407 if (player->present)
3409 while (player->MovPos)
3411 ScrollPlayer(player, SCROLL_GO_ON);
3412 ScrollScreen(NULL, SCROLL_GO_ON);
3414 AdvanceFrameAndPlayerCounters(player->index_nr);
3419 Delay(wait_delay_value);
3422 DrawPlayer(player); /* needed here only to cleanup last field */
3423 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3425 player->is_moving = FALSE;
3428 if (IS_CUSTOM_ELEMENT(old_element))
3429 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3431 player->index_bit, leave_side);
3433 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3435 player->index_bit, leave_side);
3437 Feld[jx][jy] = el_player;
3438 InitPlayerField(jx, jy, el_player, TRUE);
3440 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3442 Feld[jx][jy] = element;
3443 InitField(jx, jy, FALSE);
3446 /* only visually relocate centered player */
3447 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3448 level.instant_relocation);
3450 TestIfPlayerTouchesBadThing(jx, jy);
3451 TestIfPlayerTouchesCustomElement(jx, jy);
3453 if (IS_CUSTOM_ELEMENT(element))
3454 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3455 player->index_bit, enter_side);
3457 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3458 player->index_bit, enter_side);
3461 void Explode(int ex, int ey, int phase, int mode)
3467 /* !!! eliminate this variable !!! */
3468 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3470 if (game.explosions_delayed)
3472 ExplodeField[ex][ey] = mode;
3476 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3478 int center_element = Feld[ex][ey];
3479 int artwork_element, explosion_element; /* set these values later */
3482 /* --- This is only really needed (and now handled) in "Impact()". --- */
3483 /* do not explode moving elements that left the explode field in time */
3484 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3485 center_element == EL_EMPTY &&
3486 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3491 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3492 if (mode == EX_TYPE_NORMAL ||
3493 mode == EX_TYPE_CENTER ||
3494 mode == EX_TYPE_CROSS)
3495 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3498 /* remove things displayed in background while burning dynamite */
3499 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3502 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3504 /* put moving element to center field (and let it explode there) */
3505 center_element = MovingOrBlocked2Element(ex, ey);
3506 RemoveMovingField(ex, ey);
3507 Feld[ex][ey] = center_element;
3510 /* now "center_element" is finally determined -- set related values now */
3511 artwork_element = center_element; /* for custom player artwork */
3512 explosion_element = center_element; /* for custom player artwork */
3514 if (IS_PLAYER(ex, ey))
3516 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3518 artwork_element = stored_player[player_nr].artwork_element;
3520 if (level.use_explosion_element[player_nr])
3522 explosion_element = level.explosion_element[player_nr];
3523 artwork_element = explosion_element;
3528 if (mode == EX_TYPE_NORMAL ||
3529 mode == EX_TYPE_CENTER ||
3530 mode == EX_TYPE_CROSS)
3531 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3534 last_phase = element_info[explosion_element].explosion_delay + 1;
3536 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3538 int xx = x - ex + 1;
3539 int yy = y - ey + 1;
3542 if (!IN_LEV_FIELD(x, y) ||
3543 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3544 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3547 element = Feld[x][y];
3549 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3551 element = MovingOrBlocked2Element(x, y);
3553 if (!IS_EXPLOSION_PROOF(element))
3554 RemoveMovingField(x, y);
3557 /* indestructible elements can only explode in center (but not flames) */
3558 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3559 mode == EX_TYPE_BORDER)) ||
3560 element == EL_FLAMES)
3563 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3564 behaviour, for example when touching a yamyam that explodes to rocks
3565 with active deadly shield, a rock is created under the player !!! */
3566 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3568 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3569 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3570 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3572 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3575 if (IS_ACTIVE_BOMB(element))
3577 /* re-activate things under the bomb like gate or penguin */
3578 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3585 /* save walkable background elements while explosion on same tile */
3586 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3587 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3588 Back[x][y] = element;
3590 /* ignite explodable elements reached by other explosion */
3591 if (element == EL_EXPLOSION)
3592 element = Store2[x][y];
3594 if (AmoebaNr[x][y] &&
3595 (element == EL_AMOEBA_FULL ||
3596 element == EL_BD_AMOEBA ||
3597 element == EL_AMOEBA_GROWING))
3599 AmoebaCnt[AmoebaNr[x][y]]--;
3600 AmoebaCnt2[AmoebaNr[x][y]]--;
3605 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3607 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3609 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3611 if (PLAYERINFO(ex, ey)->use_murphy)
3612 Store[x][y] = EL_EMPTY;
3615 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3616 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3617 else if (ELEM_IS_PLAYER(center_element))
3618 Store[x][y] = EL_EMPTY;
3619 else if (center_element == EL_YAMYAM)
3620 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3621 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3622 Store[x][y] = element_info[center_element].content.e[xx][yy];
3624 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3625 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3626 otherwise) -- FIX THIS !!! */
3627 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3628 Store[x][y] = element_info[element].content.e[1][1];
3630 else if (!CAN_EXPLODE(element))
3631 Store[x][y] = element_info[element].content.e[1][1];
3634 Store[x][y] = EL_EMPTY;
3636 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3637 center_element == EL_AMOEBA_TO_DIAMOND)
3638 Store2[x][y] = element;
3640 Feld[x][y] = EL_EXPLOSION;
3641 GfxElement[x][y] = artwork_element;
3643 ExplodePhase[x][y] = 1;
3644 ExplodeDelay[x][y] = last_phase;
3649 if (center_element == EL_YAMYAM)
3650 game.yamyam_content_nr =
3651 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3663 GfxFrame[x][y] = 0; /* restart explosion animation */
3665 last_phase = ExplodeDelay[x][y];
3667 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3671 /* activate this even in non-DEBUG version until cause for crash in
3672 getGraphicAnimationFrame() (see below) is found and eliminated */
3678 /* this can happen if the player leaves an explosion just in time */
3679 if (GfxElement[x][y] == EL_UNDEFINED)
3680 GfxElement[x][y] = EL_EMPTY;
3682 if (GfxElement[x][y] == EL_UNDEFINED)
3685 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3686 printf("Explode(): This should never happen!\n");
3689 GfxElement[x][y] = EL_EMPTY;
3695 border_element = Store2[x][y];
3696 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3697 border_element = StorePlayer[x][y];
3699 if (phase == element_info[border_element].ignition_delay ||
3700 phase == last_phase)
3702 boolean border_explosion = FALSE;
3704 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3705 !PLAYER_EXPLOSION_PROTECTED(x, y))
3707 KillPlayerUnlessExplosionProtected(x, y);
3708 border_explosion = TRUE;
3710 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3712 Feld[x][y] = Store2[x][y];
3715 border_explosion = TRUE;
3717 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3719 AmoebeUmwandeln(x, y);
3721 border_explosion = TRUE;
3724 /* if an element just explodes due to another explosion (chain-reaction),
3725 do not immediately end the new explosion when it was the last frame of
3726 the explosion (as it would be done in the following "if"-statement!) */
3727 if (border_explosion && phase == last_phase)
3731 if (phase == last_phase)
3735 element = Feld[x][y] = Store[x][y];
3736 Store[x][y] = Store2[x][y] = 0;
3737 GfxElement[x][y] = EL_UNDEFINED;
3739 /* player can escape from explosions and might therefore be still alive */
3740 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3741 element <= EL_PLAYER_IS_EXPLODING_4)
3743 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3744 int explosion_element = EL_PLAYER_1 + player_nr;
3745 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3746 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3748 if (level.use_explosion_element[player_nr])
3749 explosion_element = level.explosion_element[player_nr];
3751 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3752 element_info[explosion_element].content.e[xx][yy]);
3755 /* restore probably existing indestructible background element */
3756 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3757 element = Feld[x][y] = Back[x][y];
3760 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3761 GfxDir[x][y] = MV_NONE;
3762 ChangeDelay[x][y] = 0;
3763 ChangePage[x][y] = -1;
3765 #if USE_NEW_CUSTOM_VALUE
3766 CustomValue[x][y] = 0;
3769 InitField_WithBug2(x, y, FALSE);
3771 DrawLevelField(x, y);
3773 TestIfElementTouchesCustomElement(x, y);
3775 if (GFX_CRUMBLED(element))
3776 DrawLevelFieldCrumbledSandNeighbours(x, y);
3778 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3779 StorePlayer[x][y] = 0;
3781 if (ELEM_IS_PLAYER(element))
3782 RelocatePlayer(x, y, element);
3784 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3786 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3787 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3790 DrawLevelFieldCrumbledSand(x, y);
3792 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3794 DrawLevelElement(x, y, Back[x][y]);
3795 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3797 else if (IS_WALKABLE_UNDER(Back[x][y]))
3799 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3800 DrawLevelElementThruMask(x, y, Back[x][y]);
3802 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3803 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3807 void DynaExplode(int ex, int ey)
3810 int dynabomb_element = Feld[ex][ey];
3811 int dynabomb_size = 1;
3812 boolean dynabomb_xl = FALSE;
3813 struct PlayerInfo *player;
3814 static int xy[4][2] =
3822 if (IS_ACTIVE_BOMB(dynabomb_element))
3824 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3825 dynabomb_size = player->dynabomb_size;
3826 dynabomb_xl = player->dynabomb_xl;
3827 player->dynabombs_left++;
3830 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3832 for (i = 0; i < NUM_DIRECTIONS; i++)
3834 for (j = 1; j <= dynabomb_size; j++)
3836 int x = ex + j * xy[i][0];
3837 int y = ey + j * xy[i][1];
3840 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3843 element = Feld[x][y];
3845 /* do not restart explosions of fields with active bombs */
3846 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3849 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3851 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3852 !IS_DIGGABLE(element) && !dynabomb_xl)
3858 void Bang(int x, int y)
3860 int element = MovingOrBlocked2Element(x, y);
3861 int explosion_type = EX_TYPE_NORMAL;
3863 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3865 struct PlayerInfo *player = PLAYERINFO(x, y);
3867 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3868 player->element_nr);
3870 if (level.use_explosion_element[player->index_nr])
3872 int explosion_element = level.explosion_element[player->index_nr];
3874 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3875 explosion_type = EX_TYPE_CROSS;
3876 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3877 explosion_type = EX_TYPE_CENTER;
3885 case EL_BD_BUTTERFLY:
3888 case EL_DARK_YAMYAM:
3892 RaiseScoreElement(element);
3895 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3896 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3897 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3898 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3899 case EL_DYNABOMB_INCREASE_NUMBER:
3900 case EL_DYNABOMB_INCREASE_SIZE:
3901 case EL_DYNABOMB_INCREASE_POWER:
3902 explosion_type = EX_TYPE_DYNA;
3907 case EL_LAMP_ACTIVE:
3908 case EL_AMOEBA_TO_DIAMOND:
3909 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3910 explosion_type = EX_TYPE_CENTER;
3914 if (element_info[element].explosion_type == EXPLODES_CROSS)
3915 explosion_type = EX_TYPE_CROSS;
3916 else if (element_info[element].explosion_type == EXPLODES_1X1)
3917 explosion_type = EX_TYPE_CENTER;
3921 if (explosion_type == EX_TYPE_DYNA)
3924 Explode(x, y, EX_PHASE_START, explosion_type);
3926 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3929 void SplashAcid(int x, int y)
3931 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3932 (!IN_LEV_FIELD(x - 1, y - 2) ||
3933 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3934 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3936 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3937 (!IN_LEV_FIELD(x + 1, y - 2) ||
3938 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3939 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3941 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3944 static void InitBeltMovement()
3946 static int belt_base_element[4] =
3948 EL_CONVEYOR_BELT_1_LEFT,
3949 EL_CONVEYOR_BELT_2_LEFT,
3950 EL_CONVEYOR_BELT_3_LEFT,
3951 EL_CONVEYOR_BELT_4_LEFT
3953 static int belt_base_active_element[4] =
3955 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3956 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3957 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3958 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3963 /* set frame order for belt animation graphic according to belt direction */
3964 for (i = 0; i < NUM_BELTS; i++)
3968 for (j = 0; j < NUM_BELT_PARTS; j++)
3970 int element = belt_base_active_element[belt_nr] + j;
3971 int graphic = el2img(element);
3973 if (game.belt_dir[i] == MV_LEFT)
3974 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3976 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3980 SCAN_PLAYFIELD(x, y)
3982 int element = Feld[x][y];
3984 for (i = 0; i < NUM_BELTS; i++)
3986 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3988 int e_belt_nr = getBeltNrFromBeltElement(element);
3991 if (e_belt_nr == belt_nr)
3993 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3995 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4002 static void ToggleBeltSwitch(int x, int y)
4004 static int belt_base_element[4] =
4006 EL_CONVEYOR_BELT_1_LEFT,
4007 EL_CONVEYOR_BELT_2_LEFT,
4008 EL_CONVEYOR_BELT_3_LEFT,
4009 EL_CONVEYOR_BELT_4_LEFT
4011 static int belt_base_active_element[4] =
4013 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4014 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4015 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4016 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4018 static int belt_base_switch_element[4] =
4020 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4021 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4022 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4023 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4025 static int belt_move_dir[4] =
4033 int element = Feld[x][y];
4034 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4035 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4036 int belt_dir = belt_move_dir[belt_dir_nr];
4039 if (!IS_BELT_SWITCH(element))
4042 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4043 game.belt_dir[belt_nr] = belt_dir;
4045 if (belt_dir_nr == 3)
4048 /* set frame order for belt animation graphic according to belt direction */
4049 for (i = 0; i < NUM_BELT_PARTS; i++)
4051 int element = belt_base_active_element[belt_nr] + i;
4052 int graphic = el2img(element);
4054 if (belt_dir == MV_LEFT)
4055 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4057 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4060 SCAN_PLAYFIELD(xx, yy)
4062 int element = Feld[xx][yy];
4064 if (IS_BELT_SWITCH(element))
4066 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4068 if (e_belt_nr == belt_nr)
4070 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4071 DrawLevelField(xx, yy);
4074 else if (IS_BELT(element) && belt_dir != MV_NONE)
4076 int e_belt_nr = getBeltNrFromBeltElement(element);
4078 if (e_belt_nr == belt_nr)
4080 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4082 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4083 DrawLevelField(xx, yy);
4086 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4088 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4090 if (e_belt_nr == belt_nr)
4092 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4094 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4095 DrawLevelField(xx, yy);
4101 static void ToggleSwitchgateSwitch(int x, int y)
4105 game.switchgate_pos = !game.switchgate_pos;
4107 SCAN_PLAYFIELD(xx, yy)
4109 int element = Feld[xx][yy];
4111 #if !USE_BOTH_SWITCHGATE_SWITCHES
4112 if (element == EL_SWITCHGATE_SWITCH_UP ||
4113 element == EL_SWITCHGATE_SWITCH_DOWN)
4115 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4116 DrawLevelField(xx, yy);
4119 if (element == EL_SWITCHGATE_SWITCH_UP)
4121 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4122 DrawLevelField(xx, yy);
4124 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4126 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4127 DrawLevelField(xx, yy);
4130 else if (element == EL_SWITCHGATE_OPEN ||
4131 element == EL_SWITCHGATE_OPENING)
4133 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4135 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4137 else if (element == EL_SWITCHGATE_CLOSED ||
4138 element == EL_SWITCHGATE_CLOSING)
4140 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4142 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4147 static int getInvisibleActiveFromInvisibleElement(int element)
4149 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4150 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4151 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4155 static int getInvisibleFromInvisibleActiveElement(int element)
4157 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4158 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4159 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4163 static void RedrawAllLightSwitchesAndInvisibleElements()
4167 SCAN_PLAYFIELD(x, y)
4169 int element = Feld[x][y];
4171 if (element == EL_LIGHT_SWITCH &&
4172 game.light_time_left > 0)
4174 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4175 DrawLevelField(x, y);
4177 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4178 game.light_time_left == 0)
4180 Feld[x][y] = EL_LIGHT_SWITCH;
4181 DrawLevelField(x, y);
4183 else if (element == EL_EMC_DRIPPER &&
4184 game.light_time_left > 0)
4186 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4187 DrawLevelField(x, y);
4189 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4190 game.light_time_left == 0)
4192 Feld[x][y] = EL_EMC_DRIPPER;
4193 DrawLevelField(x, y);
4195 else if (element == EL_INVISIBLE_STEELWALL ||
4196 element == EL_INVISIBLE_WALL ||
4197 element == EL_INVISIBLE_SAND)
4199 if (game.light_time_left > 0)
4200 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4202 DrawLevelField(x, y);
4204 /* uncrumble neighbour fields, if needed */
4205 if (element == EL_INVISIBLE_SAND)
4206 DrawLevelFieldCrumbledSandNeighbours(x, y);
4208 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4209 element == EL_INVISIBLE_WALL_ACTIVE ||
4210 element == EL_INVISIBLE_SAND_ACTIVE)
4212 if (game.light_time_left == 0)
4213 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4215 DrawLevelField(x, y);
4217 /* re-crumble neighbour fields, if needed */
4218 if (element == EL_INVISIBLE_SAND)
4219 DrawLevelFieldCrumbledSandNeighbours(x, y);
4224 static void RedrawAllInvisibleElementsForLenses()
4228 SCAN_PLAYFIELD(x, y)
4230 int element = Feld[x][y];
4232 if (element == EL_EMC_DRIPPER &&
4233 game.lenses_time_left > 0)
4235 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4236 DrawLevelField(x, y);
4238 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4239 game.lenses_time_left == 0)
4241 Feld[x][y] = EL_EMC_DRIPPER;
4242 DrawLevelField(x, y);
4244 else if (element == EL_INVISIBLE_STEELWALL ||
4245 element == EL_INVISIBLE_WALL ||
4246 element == EL_INVISIBLE_SAND)
4248 if (game.lenses_time_left > 0)
4249 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4251 DrawLevelField(x, y);
4253 /* uncrumble neighbour fields, if needed */
4254 if (element == EL_INVISIBLE_SAND)
4255 DrawLevelFieldCrumbledSandNeighbours(x, y);
4257 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4258 element == EL_INVISIBLE_WALL_ACTIVE ||
4259 element == EL_INVISIBLE_SAND_ACTIVE)
4261 if (game.lenses_time_left == 0)
4262 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4264 DrawLevelField(x, y);
4266 /* re-crumble neighbour fields, if needed */
4267 if (element == EL_INVISIBLE_SAND)
4268 DrawLevelFieldCrumbledSandNeighbours(x, y);
4273 static void RedrawAllInvisibleElementsForMagnifier()
4277 SCAN_PLAYFIELD(x, y)
4279 int element = Feld[x][y];
4281 if (element == EL_EMC_FAKE_GRASS &&
4282 game.magnify_time_left > 0)
4284 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4285 DrawLevelField(x, y);
4287 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4288 game.magnify_time_left == 0)
4290 Feld[x][y] = EL_EMC_FAKE_GRASS;
4291 DrawLevelField(x, y);
4293 else if (IS_GATE_GRAY(element) &&
4294 game.magnify_time_left > 0)
4296 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4297 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4298 IS_EM_GATE_GRAY(element) ?
4299 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4300 IS_EMC_GATE_GRAY(element) ?
4301 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4303 DrawLevelField(x, y);
4305 else if (IS_GATE_GRAY_ACTIVE(element) &&
4306 game.magnify_time_left == 0)
4308 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4309 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4310 IS_EM_GATE_GRAY_ACTIVE(element) ?
4311 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4312 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4313 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4315 DrawLevelField(x, y);
4320 static void ToggleLightSwitch(int x, int y)
4322 int element = Feld[x][y];
4324 game.light_time_left =
4325 (element == EL_LIGHT_SWITCH ?
4326 level.time_light * FRAMES_PER_SECOND : 0);
4328 RedrawAllLightSwitchesAndInvisibleElements();
4331 static void ActivateTimegateSwitch(int x, int y)
4335 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4337 SCAN_PLAYFIELD(xx, yy)
4339 int element = Feld[xx][yy];
4341 if (element == EL_TIMEGATE_CLOSED ||
4342 element == EL_TIMEGATE_CLOSING)
4344 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4345 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4349 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4351 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4352 DrawLevelField(xx, yy);
4358 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4361 void Impact(int x, int y)
4363 boolean last_line = (y == lev_fieldy - 1);
4364 boolean object_hit = FALSE;
4365 boolean impact = (last_line || object_hit);
4366 int element = Feld[x][y];
4367 int smashed = EL_STEELWALL;
4369 if (!last_line) /* check if element below was hit */
4371 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4374 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4375 MovDir[x][y + 1] != MV_DOWN ||
4376 MovPos[x][y + 1] <= TILEY / 2));
4378 /* do not smash moving elements that left the smashed field in time */
4379 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4380 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4383 #if USE_QUICKSAND_IMPACT_BUGFIX
4384 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4386 RemoveMovingField(x, y + 1);
4387 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4388 Feld[x][y + 2] = EL_ROCK;
4389 DrawLevelField(x, y + 2);
4396 smashed = MovingOrBlocked2Element(x, y + 1);
4398 impact = (last_line || object_hit);
4401 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4403 SplashAcid(x, y + 1);
4407 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4408 /* only reset graphic animation if graphic really changes after impact */
4410 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4412 ResetGfxAnimation(x, y);
4413 DrawLevelField(x, y);
4416 if (impact && CAN_EXPLODE_IMPACT(element))
4421 else if (impact && element == EL_PEARL)
4423 ResetGfxAnimation(x, y);
4425 Feld[x][y] = EL_PEARL_BREAKING;
4426 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4429 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4431 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4436 if (impact && element == EL_AMOEBA_DROP)
4438 if (object_hit && IS_PLAYER(x, y + 1))
4439 KillPlayerUnlessEnemyProtected(x, y + 1);
4440 else if (object_hit && smashed == EL_PENGUIN)
4444 Feld[x][y] = EL_AMOEBA_GROWING;
4445 Store[x][y] = EL_AMOEBA_WET;
4447 ResetRandomAnimationValue(x, y);
4452 if (object_hit) /* check which object was hit */
4454 if (CAN_PASS_MAGIC_WALL(element) &&
4455 (smashed == EL_MAGIC_WALL ||
4456 smashed == EL_BD_MAGIC_WALL))
4459 int activated_magic_wall =
4460 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4461 EL_BD_MAGIC_WALL_ACTIVE);
4463 /* activate magic wall / mill */
4464 SCAN_PLAYFIELD(xx, yy)
4465 if (Feld[xx][yy] == smashed)
4466 Feld[xx][yy] = activated_magic_wall;
4468 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4469 game.magic_wall_active = TRUE;
4471 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4472 SND_MAGIC_WALL_ACTIVATING :
4473 SND_BD_MAGIC_WALL_ACTIVATING));
4476 if (IS_PLAYER(x, y + 1))
4478 if (CAN_SMASH_PLAYER(element))
4480 KillPlayerUnlessEnemyProtected(x, y + 1);
4484 else if (smashed == EL_PENGUIN)
4486 if (CAN_SMASH_PLAYER(element))
4492 else if (element == EL_BD_DIAMOND)
4494 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4500 else if (((element == EL_SP_INFOTRON ||
4501 element == EL_SP_ZONK) &&
4502 (smashed == EL_SP_SNIKSNAK ||
4503 smashed == EL_SP_ELECTRON ||
4504 smashed == EL_SP_DISK_ORANGE)) ||
4505 (element == EL_SP_INFOTRON &&
4506 smashed == EL_SP_DISK_YELLOW))
4511 else if (CAN_SMASH_EVERYTHING(element))
4513 if (IS_CLASSIC_ENEMY(smashed) ||
4514 CAN_EXPLODE_SMASHED(smashed))
4519 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4521 if (smashed == EL_LAMP ||
4522 smashed == EL_LAMP_ACTIVE)
4527 else if (smashed == EL_NUT)
4529 Feld[x][y + 1] = EL_NUT_BREAKING;
4530 PlayLevelSound(x, y, SND_NUT_BREAKING);
4531 RaiseScoreElement(EL_NUT);
4534 else if (smashed == EL_PEARL)
4536 ResetGfxAnimation(x, y);
4538 Feld[x][y + 1] = EL_PEARL_BREAKING;
4539 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4542 else if (smashed == EL_DIAMOND)
4544 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4545 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4548 else if (IS_BELT_SWITCH(smashed))
4550 ToggleBeltSwitch(x, y + 1);
4552 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4553 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4555 ToggleSwitchgateSwitch(x, y + 1);
4557 else if (smashed == EL_LIGHT_SWITCH ||
4558 smashed == EL_LIGHT_SWITCH_ACTIVE)
4560 ToggleLightSwitch(x, y + 1);
4565 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4568 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4570 CheckElementChangeBySide(x, y + 1, smashed, element,
4571 CE_SWITCHED, CH_SIDE_TOP);
4572 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4578 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4583 /* play sound of magic wall / mill */
4585 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4586 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4588 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4589 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4590 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4591 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4596 /* play sound of object that hits the ground */
4597 if (last_line || object_hit)
4598 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4601 inline static void TurnRoundExt(int x, int y)
4613 { 0, 0 }, { 0, 0 }, { 0, 0 },
4618 int left, right, back;
4622 { MV_DOWN, MV_UP, MV_RIGHT },
4623 { MV_UP, MV_DOWN, MV_LEFT },
4625 { MV_LEFT, MV_RIGHT, MV_DOWN },
4629 { MV_RIGHT, MV_LEFT, MV_UP }
4632 int element = Feld[x][y];
4633 int move_pattern = element_info[element].move_pattern;
4635 int old_move_dir = MovDir[x][y];
4636 int left_dir = turn[old_move_dir].left;
4637 int right_dir = turn[old_move_dir].right;
4638 int back_dir = turn[old_move_dir].back;
4640 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4641 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4642 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4643 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4645 int left_x = x + left_dx, left_y = y + left_dy;
4646 int right_x = x + right_dx, right_y = y + right_dy;
4647 int move_x = x + move_dx, move_y = y + move_dy;
4651 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4653 TestIfBadThingTouchesOtherBadThing(x, y);
4655 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4656 MovDir[x][y] = right_dir;
4657 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4658 MovDir[x][y] = left_dir;
4660 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4662 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4665 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4667 TestIfBadThingTouchesOtherBadThing(x, y);
4669 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4670 MovDir[x][y] = left_dir;
4671 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4672 MovDir[x][y] = right_dir;
4674 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4676 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4679 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4681 TestIfBadThingTouchesOtherBadThing(x, y);
4683 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4684 MovDir[x][y] = left_dir;
4685 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4686 MovDir[x][y] = right_dir;
4688 if (MovDir[x][y] != old_move_dir)
4691 else if (element == EL_YAMYAM)
4693 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4694 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4696 if (can_turn_left && can_turn_right)
4697 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4698 else if (can_turn_left)
4699 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4700 else if (can_turn_right)
4701 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4703 MovDir[x][y] = back_dir;
4705 MovDelay[x][y] = 16 + 16 * RND(3);
4707 else if (element == EL_DARK_YAMYAM)
4709 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4711 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4714 if (can_turn_left && can_turn_right)
4715 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4716 else if (can_turn_left)
4717 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4718 else if (can_turn_right)
4719 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4721 MovDir[x][y] = back_dir;
4723 MovDelay[x][y] = 16 + 16 * RND(3);
4725 else if (element == EL_PACMAN)
4727 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4728 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4730 if (can_turn_left && can_turn_right)
4731 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4732 else if (can_turn_left)
4733 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4734 else if (can_turn_right)
4735 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4737 MovDir[x][y] = back_dir;
4739 MovDelay[x][y] = 6 + RND(40);
4741 else if (element == EL_PIG)
4743 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4744 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4745 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4746 boolean should_turn_left, should_turn_right, should_move_on;
4748 int rnd = RND(rnd_value);
4750 should_turn_left = (can_turn_left &&
4752 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4753 y + back_dy + left_dy)));
4754 should_turn_right = (can_turn_right &&
4756 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4757 y + back_dy + right_dy)));
4758 should_move_on = (can_move_on &&
4761 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4762 y + move_dy + left_dy) ||
4763 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4764 y + move_dy + right_dy)));
4766 if (should_turn_left || should_turn_right || should_move_on)
4768 if (should_turn_left && should_turn_right && should_move_on)
4769 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4770 rnd < 2 * rnd_value / 3 ? right_dir :
4772 else if (should_turn_left && should_turn_right)
4773 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4774 else if (should_turn_left && should_move_on)
4775 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4776 else if (should_turn_right && should_move_on)
4777 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4778 else if (should_turn_left)
4779 MovDir[x][y] = left_dir;
4780 else if (should_turn_right)
4781 MovDir[x][y] = right_dir;
4782 else if (should_move_on)
4783 MovDir[x][y] = old_move_dir;
4785 else if (can_move_on && rnd > rnd_value / 8)
4786 MovDir[x][y] = old_move_dir;
4787 else if (can_turn_left && can_turn_right)
4788 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4789 else if (can_turn_left && rnd > rnd_value / 8)
4790 MovDir[x][y] = left_dir;
4791 else if (can_turn_right && rnd > rnd_value/8)
4792 MovDir[x][y] = right_dir;
4794 MovDir[x][y] = back_dir;
4796 xx = x + move_xy[MovDir[x][y]].dx;
4797 yy = y + move_xy[MovDir[x][y]].dy;
4799 if (!IN_LEV_FIELD(xx, yy) ||
4800 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4801 MovDir[x][y] = old_move_dir;
4805 else if (element == EL_DRAGON)
4807 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4808 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4809 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4811 int rnd = RND(rnd_value);
4813 if (can_move_on && rnd > rnd_value / 8)
4814 MovDir[x][y] = old_move_dir;
4815 else if (can_turn_left && can_turn_right)
4816 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4817 else if (can_turn_left && rnd > rnd_value / 8)
4818 MovDir[x][y] = left_dir;
4819 else if (can_turn_right && rnd > rnd_value / 8)
4820 MovDir[x][y] = right_dir;
4822 MovDir[x][y] = back_dir;
4824 xx = x + move_xy[MovDir[x][y]].dx;
4825 yy = y + move_xy[MovDir[x][y]].dy;
4827 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4828 MovDir[x][y] = old_move_dir;
4832 else if (element == EL_MOLE)
4834 boolean can_move_on =
4835 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4836 IS_AMOEBOID(Feld[move_x][move_y]) ||
4837 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4840 boolean can_turn_left =
4841 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4842 IS_AMOEBOID(Feld[left_x][left_y])));
4844 boolean can_turn_right =
4845 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4846 IS_AMOEBOID(Feld[right_x][right_y])));
4848 if (can_turn_left && can_turn_right)
4849 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4850 else if (can_turn_left)
4851 MovDir[x][y] = left_dir;
4853 MovDir[x][y] = right_dir;
4856 if (MovDir[x][y] != old_move_dir)
4859 else if (element == EL_BALLOON)
4861 MovDir[x][y] = game.wind_direction;
4864 else if (element == EL_SPRING)
4866 #if USE_NEW_SPRING_BUMPER
4867 if (MovDir[x][y] & MV_HORIZONTAL)
4869 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4870 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4872 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4873 ResetGfxAnimation(move_x, move_y);
4874 DrawLevelField(move_x, move_y);
4876 MovDir[x][y] = back_dir;
4878 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4879 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4880 MovDir[x][y] = MV_NONE;
4883 if (MovDir[x][y] & MV_HORIZONTAL &&
4884 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4885 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4886 MovDir[x][y] = MV_NONE;
4891 else if (element == EL_ROBOT ||
4892 element == EL_SATELLITE ||
4893 element == EL_PENGUIN ||
4894 element == EL_EMC_ANDROID)
4896 int attr_x = -1, attr_y = -1;
4907 for (i = 0; i < MAX_PLAYERS; i++)
4909 struct PlayerInfo *player = &stored_player[i];
4910 int jx = player->jx, jy = player->jy;
4912 if (!player->active)
4916 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4924 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4925 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4926 game.engine_version < VERSION_IDENT(3,1,0,0)))
4932 if (element == EL_PENGUIN)
4935 static int xy[4][2] =
4943 for (i = 0; i < NUM_DIRECTIONS; i++)
4945 int ex = x + xy[i][0];
4946 int ey = y + xy[i][1];
4948 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4957 MovDir[x][y] = MV_NONE;
4959 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4960 else if (attr_x > x)
4961 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4963 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4964 else if (attr_y > y)
4965 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4967 if (element == EL_ROBOT)
4971 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4972 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4973 Moving2Blocked(x, y, &newx, &newy);
4975 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4976 MovDelay[x][y] = 8 + 8 * !RND(3);
4978 MovDelay[x][y] = 16;
4980 else if (element == EL_PENGUIN)
4986 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4988 boolean first_horiz = RND(2);
4989 int new_move_dir = MovDir[x][y];
4992 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4993 Moving2Blocked(x, y, &newx, &newy);
4995 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4999 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5000 Moving2Blocked(x, y, &newx, &newy);
5002 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5005 MovDir[x][y] = old_move_dir;
5009 else if (element == EL_SATELLITE)
5015 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5017 boolean first_horiz = RND(2);
5018 int new_move_dir = MovDir[x][y];
5021 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5022 Moving2Blocked(x, y, &newx, &newy);
5024 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5028 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5029 Moving2Blocked(x, y, &newx, &newy);
5031 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5034 MovDir[x][y] = old_move_dir;
5038 else if (element == EL_EMC_ANDROID)
5040 static int check_pos[16] =
5042 -1, /* 0 => (invalid) */
5043 7, /* 1 => MV_LEFT */
5044 3, /* 2 => MV_RIGHT */
5045 -1, /* 3 => (invalid) */
5047 0, /* 5 => MV_LEFT | MV_UP */
5048 2, /* 6 => MV_RIGHT | MV_UP */
5049 -1, /* 7 => (invalid) */
5050 5, /* 8 => MV_DOWN */
5051 6, /* 9 => MV_LEFT | MV_DOWN */
5052 4, /* 10 => MV_RIGHT | MV_DOWN */
5053 -1, /* 11 => (invalid) */
5054 -1, /* 12 => (invalid) */
5055 -1, /* 13 => (invalid) */
5056 -1, /* 14 => (invalid) */
5057 -1, /* 15 => (invalid) */
5065 { -1, -1, MV_LEFT | MV_UP },
5067 { +1, -1, MV_RIGHT | MV_UP },
5068 { +1, 0, MV_RIGHT },
5069 { +1, +1, MV_RIGHT | MV_DOWN },
5071 { -1, +1, MV_LEFT | MV_DOWN },
5074 int start_pos, check_order;
5075 boolean can_clone = FALSE;
5078 /* check if there is any free field around current position */
5079 for (i = 0; i < 8; i++)
5081 int newx = x + check_xy[i].dx;
5082 int newy = y + check_xy[i].dy;
5084 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5092 if (can_clone) /* randomly find an element to clone */
5096 start_pos = check_pos[RND(8)];
5097 check_order = (RND(2) ? -1 : +1);
5099 for (i = 0; i < 8; i++)
5101 int pos_raw = start_pos + i * check_order;
5102 int pos = (pos_raw + 8) % 8;
5103 int newx = x + check_xy[pos].dx;
5104 int newy = y + check_xy[pos].dy;
5106 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5108 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5109 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5111 Store[x][y] = Feld[newx][newy];
5120 if (can_clone) /* randomly find a direction to move */
5124 start_pos = check_pos[RND(8)];
5125 check_order = (RND(2) ? -1 : +1);
5127 for (i = 0; i < 8; i++)
5129 int pos_raw = start_pos + i * check_order;
5130 int pos = (pos_raw + 8) % 8;
5131 int newx = x + check_xy[pos].dx;
5132 int newy = y + check_xy[pos].dy;
5133 int new_move_dir = check_xy[pos].dir;
5135 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5137 MovDir[x][y] = new_move_dir;
5138 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5147 if (can_clone) /* cloning and moving successful */
5150 /* cannot clone -- try to move towards player */
5152 start_pos = check_pos[MovDir[x][y] & 0x0f];
5153 check_order = (RND(2) ? -1 : +1);
5155 for (i = 0; i < 3; i++)
5157 /* first check start_pos, then previous/next or (next/previous) pos */
5158 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5159 int pos = (pos_raw + 8) % 8;
5160 int newx = x + check_xy[pos].dx;
5161 int newy = y + check_xy[pos].dy;
5162 int new_move_dir = check_xy[pos].dir;
5164 if (IS_PLAYER(newx, newy))
5167 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5169 MovDir[x][y] = new_move_dir;
5170 MovDelay[x][y] = level.android_move_time * 8 + 1;
5177 else if (move_pattern == MV_TURNING_LEFT ||
5178 move_pattern == MV_TURNING_RIGHT ||
5179 move_pattern == MV_TURNING_LEFT_RIGHT ||
5180 move_pattern == MV_TURNING_RIGHT_LEFT ||
5181 move_pattern == MV_TURNING_RANDOM ||
5182 move_pattern == MV_ALL_DIRECTIONS)
5184 boolean can_turn_left =
5185 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5186 boolean can_turn_right =
5187 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5189 if (element_info[element].move_stepsize == 0) /* "not moving" */
5192 if (move_pattern == MV_TURNING_LEFT)
5193 MovDir[x][y] = left_dir;
5194 else if (move_pattern == MV_TURNING_RIGHT)
5195 MovDir[x][y] = right_dir;
5196 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5197 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5198 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5199 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5200 else if (move_pattern == MV_TURNING_RANDOM)
5201 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5202 can_turn_right && !can_turn_left ? right_dir :
5203 RND(2) ? left_dir : right_dir);
5204 else if (can_turn_left && can_turn_right)
5205 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5206 else if (can_turn_left)
5207 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5208 else if (can_turn_right)
5209 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5211 MovDir[x][y] = back_dir;
5213 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5215 else if (move_pattern == MV_HORIZONTAL ||
5216 move_pattern == MV_VERTICAL)
5218 if (move_pattern & old_move_dir)
5219 MovDir[x][y] = back_dir;
5220 else if (move_pattern == MV_HORIZONTAL)
5221 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5222 else if (move_pattern == MV_VERTICAL)
5223 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5225 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5227 else if (move_pattern & MV_ANY_DIRECTION)
5229 MovDir[x][y] = move_pattern;
5230 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5232 else if (move_pattern & MV_WIND_DIRECTION)
5234 MovDir[x][y] = game.wind_direction;
5235 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5237 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5239 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5240 MovDir[x][y] = left_dir;
5241 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5242 MovDir[x][y] = right_dir;
5244 if (MovDir[x][y] != old_move_dir)
5245 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5247 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5249 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5250 MovDir[x][y] = right_dir;
5251 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5252 MovDir[x][y] = left_dir;
5254 if (MovDir[x][y] != old_move_dir)
5255 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5257 else if (move_pattern == MV_TOWARDS_PLAYER ||
5258 move_pattern == MV_AWAY_FROM_PLAYER)
5260 int attr_x = -1, attr_y = -1;
5262 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5273 for (i = 0; i < MAX_PLAYERS; i++)
5275 struct PlayerInfo *player = &stored_player[i];
5276 int jx = player->jx, jy = player->jy;
5278 if (!player->active)
5282 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5290 MovDir[x][y] = MV_NONE;
5292 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5293 else if (attr_x > x)
5294 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5296 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5297 else if (attr_y > y)
5298 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5300 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5302 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5304 boolean first_horiz = RND(2);
5305 int new_move_dir = MovDir[x][y];
5307 if (element_info[element].move_stepsize == 0) /* "not moving" */
5309 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5310 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5316 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5317 Moving2Blocked(x, y, &newx, &newy);
5319 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5323 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5324 Moving2Blocked(x, y, &newx, &newy);
5326 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5329 MovDir[x][y] = old_move_dir;
5332 else if (move_pattern == MV_WHEN_PUSHED ||
5333 move_pattern == MV_WHEN_DROPPED)
5335 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5336 MovDir[x][y] = MV_NONE;
5340 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5342 static int test_xy[7][2] =
5352 static int test_dir[7] =
5362 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5363 int move_preference = -1000000; /* start with very low preference */
5364 int new_move_dir = MV_NONE;
5365 int start_test = RND(4);
5368 for (i = 0; i < NUM_DIRECTIONS; i++)
5370 int move_dir = test_dir[start_test + i];
5371 int move_dir_preference;
5373 xx = x + test_xy[start_test + i][0];
5374 yy = y + test_xy[start_test + i][1];
5376 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5377 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5379 new_move_dir = move_dir;
5384 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5387 move_dir_preference = -1 * RunnerVisit[xx][yy];
5388 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5389 move_dir_preference = PlayerVisit[xx][yy];
5391 if (move_dir_preference > move_preference)
5393 /* prefer field that has not been visited for the longest time */
5394 move_preference = move_dir_preference;
5395 new_move_dir = move_dir;
5397 else if (move_dir_preference == move_preference &&
5398 move_dir == old_move_dir)
5400 /* prefer last direction when all directions are preferred equally */
5401 move_preference = move_dir_preference;
5402 new_move_dir = move_dir;
5406 MovDir[x][y] = new_move_dir;
5407 if (old_move_dir != new_move_dir)
5408 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5412 static void TurnRound(int x, int y)
5414 int direction = MovDir[x][y];
5418 GfxDir[x][y] = MovDir[x][y];
5420 if (direction != MovDir[x][y])
5424 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5426 ResetGfxFrame(x, y, FALSE);
5429 static boolean JustBeingPushed(int x, int y)
5433 for (i = 0; i < MAX_PLAYERS; i++)
5435 struct PlayerInfo *player = &stored_player[i];
5437 if (player->active && player->is_pushing && player->MovPos)
5439 int next_jx = player->jx + (player->jx - player->last_jx);
5440 int next_jy = player->jy + (player->jy - player->last_jy);
5442 if (x == next_jx && y == next_jy)
5450 void StartMoving(int x, int y)
5452 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5453 int element = Feld[x][y];
5458 if (MovDelay[x][y] == 0)
5459 GfxAction[x][y] = ACTION_DEFAULT;
5461 if (CAN_FALL(element) && y < lev_fieldy - 1)
5463 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5464 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5465 if (JustBeingPushed(x, y))
5468 if (element == EL_QUICKSAND_FULL)
5470 if (IS_FREE(x, y + 1))
5472 InitMovingField(x, y, MV_DOWN);
5473 started_moving = TRUE;
5475 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5476 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5477 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5478 Store[x][y] = EL_ROCK;
5480 Store[x][y] = EL_ROCK;
5483 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5485 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5487 if (!MovDelay[x][y])
5488 MovDelay[x][y] = TILEY + 1;
5497 Feld[x][y] = EL_QUICKSAND_EMPTY;
5498 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5499 Store[x][y + 1] = Store[x][y];
5502 PlayLevelSoundAction(x, y, ACTION_FILLING);
5505 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5506 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5508 InitMovingField(x, y, MV_DOWN);
5509 started_moving = TRUE;
5511 Feld[x][y] = EL_QUICKSAND_FILLING;
5512 Store[x][y] = element;
5514 PlayLevelSoundAction(x, y, ACTION_FILLING);
5516 else if (element == EL_MAGIC_WALL_FULL)
5518 if (IS_FREE(x, y + 1))
5520 InitMovingField(x, y, MV_DOWN);
5521 started_moving = TRUE;
5523 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5524 Store[x][y] = EL_CHANGED(Store[x][y]);
5526 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5528 if (!MovDelay[x][y])
5529 MovDelay[x][y] = TILEY/4 + 1;
5538 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5539 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5540 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5544 else if (element == EL_BD_MAGIC_WALL_FULL)
5546 if (IS_FREE(x, y + 1))
5548 InitMovingField(x, y, MV_DOWN);
5549 started_moving = TRUE;
5551 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5552 Store[x][y] = EL_CHANGED2(Store[x][y]);
5554 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5556 if (!MovDelay[x][y])
5557 MovDelay[x][y] = TILEY/4 + 1;
5566 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5567 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5568 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5572 else if (CAN_PASS_MAGIC_WALL(element) &&
5573 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5574 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5576 InitMovingField(x, y, MV_DOWN);
5577 started_moving = TRUE;
5580 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5581 EL_BD_MAGIC_WALL_FILLING);
5582 Store[x][y] = element;
5584 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5586 SplashAcid(x, y + 1);
5588 InitMovingField(x, y, MV_DOWN);
5589 started_moving = TRUE;
5591 Store[x][y] = EL_ACID;
5593 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5594 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5596 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5597 CAN_FALL(element) && WasJustFalling[x][y] &&
5598 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5600 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5601 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5602 (Feld[x][y + 1] == EL_BLOCKED)))
5604 /* this is needed for a special case not covered by calling "Impact()"
5605 from "ContinueMoving()": if an element moves to a tile directly below
5606 another element which was just falling on that tile (which was empty
5607 in the previous frame), the falling element above would just stop
5608 instead of smashing the element below (in previous version, the above
5609 element was just checked for "moving" instead of "falling", resulting
5610 in incorrect smashes caused by horizontal movement of the above
5611 element; also, the case of the player being the element to smash was
5612 simply not covered here... :-/ ) */
5614 CheckCollision[x][y] = 0;
5618 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5620 if (MovDir[x][y] == MV_NONE)
5622 InitMovingField(x, y, MV_DOWN);
5623 started_moving = TRUE;
5626 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5628 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5629 MovDir[x][y] = MV_DOWN;
5631 InitMovingField(x, y, MV_DOWN);
5632 started_moving = TRUE;
5634 else if (element == EL_AMOEBA_DROP)
5636 Feld[x][y] = EL_AMOEBA_GROWING;
5637 Store[x][y] = EL_AMOEBA_WET;
5639 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5640 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5641 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5642 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5644 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5645 (IS_FREE(x - 1, y + 1) ||
5646 Feld[x - 1][y + 1] == EL_ACID));
5647 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5648 (IS_FREE(x + 1, y + 1) ||
5649 Feld[x + 1][y + 1] == EL_ACID));
5650 boolean can_fall_any = (can_fall_left || can_fall_right);
5651 boolean can_fall_both = (can_fall_left && can_fall_right);
5652 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5654 #if USE_NEW_ALL_SLIPPERY
5655 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5657 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5658 can_fall_right = FALSE;
5659 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5660 can_fall_left = FALSE;
5661 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5662 can_fall_right = FALSE;
5663 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5664 can_fall_left = FALSE;
5666 can_fall_any = (can_fall_left || can_fall_right);
5667 can_fall_both = FALSE;
5670 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5672 if (slippery_type == SLIPPERY_ONLY_LEFT)
5673 can_fall_right = FALSE;
5674 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5675 can_fall_left = FALSE;
5676 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5677 can_fall_right = FALSE;
5678 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5679 can_fall_left = FALSE;
5681 can_fall_any = (can_fall_left || can_fall_right);
5682 can_fall_both = (can_fall_left && can_fall_right);
5686 #if USE_NEW_ALL_SLIPPERY
5688 #if USE_NEW_SP_SLIPPERY
5689 /* !!! better use the same properties as for custom elements here !!! */
5690 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5691 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5693 can_fall_right = FALSE; /* slip down on left side */
5694 can_fall_both = FALSE;
5699 #if USE_NEW_ALL_SLIPPERY
5702 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5703 can_fall_right = FALSE; /* slip down on left side */
5705 can_fall_left = !(can_fall_right = RND(2));
5707 can_fall_both = FALSE;
5712 if (game.emulation == EMU_BOULDERDASH ||
5713 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5714 can_fall_right = FALSE; /* slip down on left side */
5716 can_fall_left = !(can_fall_right = RND(2));
5718 can_fall_both = FALSE;
5724 /* if not determined otherwise, prefer left side for slipping down */
5725 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5726 started_moving = TRUE;
5730 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5732 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5735 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5736 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5737 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5738 int belt_dir = game.belt_dir[belt_nr];
5740 if ((belt_dir == MV_LEFT && left_is_free) ||
5741 (belt_dir == MV_RIGHT && right_is_free))
5743 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5745 InitMovingField(x, y, belt_dir);
5746 started_moving = TRUE;
5748 Pushed[x][y] = TRUE;
5749 Pushed[nextx][y] = TRUE;
5751 GfxAction[x][y] = ACTION_DEFAULT;
5755 MovDir[x][y] = 0; /* if element was moving, stop it */
5760 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5762 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5764 if (CAN_MOVE(element) && !started_moving)
5767 int move_pattern = element_info[element].move_pattern;
5772 if (MovDir[x][y] == MV_NONE)
5774 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5775 x, y, element, element_info[element].token_name);
5776 printf("StartMoving(): This should never happen!\n");
5781 Moving2Blocked(x, y, &newx, &newy);
5783 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5786 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5787 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5789 WasJustMoving[x][y] = 0;
5790 CheckCollision[x][y] = 0;
5792 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5794 if (Feld[x][y] != element) /* element has changed */
5798 if (!MovDelay[x][y]) /* start new movement phase */
5800 /* all objects that can change their move direction after each step
5801 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5803 if (element != EL_YAMYAM &&
5804 element != EL_DARK_YAMYAM &&
5805 element != EL_PACMAN &&
5806 !(move_pattern & MV_ANY_DIRECTION) &&
5807 move_pattern != MV_TURNING_LEFT &&
5808 move_pattern != MV_TURNING_RIGHT &&
5809 move_pattern != MV_TURNING_LEFT_RIGHT &&
5810 move_pattern != MV_TURNING_RIGHT_LEFT &&
5811 move_pattern != MV_TURNING_RANDOM)
5815 if (MovDelay[x][y] && (element == EL_BUG ||
5816 element == EL_SPACESHIP ||
5817 element == EL_SP_SNIKSNAK ||
5818 element == EL_SP_ELECTRON ||
5819 element == EL_MOLE))
5820 DrawLevelField(x, y);
5824 if (MovDelay[x][y]) /* wait some time before next movement */
5828 if (element == EL_ROBOT ||
5829 element == EL_YAMYAM ||
5830 element == EL_DARK_YAMYAM)
5832 DrawLevelElementAnimationIfNeeded(x, y, element);
5833 PlayLevelSoundAction(x, y, ACTION_WAITING);
5835 else if (element == EL_SP_ELECTRON)
5836 DrawLevelElementAnimationIfNeeded(x, y, element);
5837 else if (element == EL_DRAGON)
5840 int dir = MovDir[x][y];
5841 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5842 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5843 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5844 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5845 dir == MV_UP ? IMG_FLAMES_1_UP :
5846 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5847 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5849 GfxAction[x][y] = ACTION_ATTACKING;
5851 if (IS_PLAYER(x, y))
5852 DrawPlayerField(x, y);
5854 DrawLevelField(x, y);
5856 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5858 for (i = 1; i <= 3; i++)
5860 int xx = x + i * dx;
5861 int yy = y + i * dy;
5862 int sx = SCREENX(xx);
5863 int sy = SCREENY(yy);
5864 int flame_graphic = graphic + (i - 1);
5866 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5871 int flamed = MovingOrBlocked2Element(xx, yy);
5875 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5877 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5878 RemoveMovingField(xx, yy);
5880 RemoveField(xx, yy);
5882 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5885 RemoveMovingField(xx, yy);
5888 ChangeDelay[xx][yy] = 0;
5890 Feld[xx][yy] = EL_FLAMES;
5892 if (IN_SCR_FIELD(sx, sy))
5894 DrawLevelFieldCrumbledSand(xx, yy);
5895 DrawGraphic(sx, sy, flame_graphic, frame);
5900 if (Feld[xx][yy] == EL_FLAMES)
5901 Feld[xx][yy] = EL_EMPTY;
5902 DrawLevelField(xx, yy);
5907 if (MovDelay[x][y]) /* element still has to wait some time */
5909 PlayLevelSoundAction(x, y, ACTION_WAITING);
5915 /* now make next step */
5917 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5919 if (DONT_COLLIDE_WITH(element) &&
5920 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5921 !PLAYER_ENEMY_PROTECTED(newx, newy))
5923 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5928 else if (CAN_MOVE_INTO_ACID(element) &&
5929 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5930 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5931 (MovDir[x][y] == MV_DOWN ||
5932 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5934 SplashAcid(newx, newy);
5935 Store[x][y] = EL_ACID;
5937 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5939 if (Feld[newx][newy] == EL_EXIT_OPEN)
5942 DrawLevelField(x, y);
5944 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5945 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5946 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5948 local_player->friends_still_needed--;
5949 if (!local_player->friends_still_needed &&
5950 !local_player->GameOver && AllPlayersGone)
5951 PlayerWins(local_player);
5955 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5957 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5958 DrawLevelField(newx, newy);
5960 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5962 else if (!IS_FREE(newx, newy))
5964 GfxAction[x][y] = ACTION_WAITING;
5966 if (IS_PLAYER(x, y))
5967 DrawPlayerField(x, y);
5969 DrawLevelField(x, y);
5974 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5976 if (IS_FOOD_PIG(Feld[newx][newy]))
5978 if (IS_MOVING(newx, newy))
5979 RemoveMovingField(newx, newy);
5982 Feld[newx][newy] = EL_EMPTY;
5983 DrawLevelField(newx, newy);
5986 PlayLevelSound(x, y, SND_PIG_DIGGING);
5988 else if (!IS_FREE(newx, newy))
5990 if (IS_PLAYER(x, y))
5991 DrawPlayerField(x, y);
5993 DrawLevelField(x, y);
5998 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6000 if (Store[x][y] != EL_EMPTY)
6002 boolean can_clone = FALSE;
6005 /* check if element to clone is still there */
6006 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6008 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6016 /* cannot clone or target field not free anymore -- do not clone */
6017 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6018 Store[x][y] = EL_EMPTY;
6021 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6023 if (IS_MV_DIAGONAL(MovDir[x][y]))
6025 int diagonal_move_dir = MovDir[x][y];
6026 int stored = Store[x][y];
6027 int change_delay = 8;
6030 /* android is moving diagonally */
6032 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6034 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6035 GfxElement[x][y] = EL_EMC_ANDROID;
6036 GfxAction[x][y] = ACTION_SHRINKING;
6037 GfxDir[x][y] = diagonal_move_dir;
6038 ChangeDelay[x][y] = change_delay;
6040 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6043 DrawLevelGraphicAnimation(x, y, graphic);
6044 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6046 if (Feld[newx][newy] == EL_ACID)
6048 SplashAcid(newx, newy);
6053 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6055 Store[newx][newy] = EL_EMC_ANDROID;
6056 GfxElement[newx][newy] = EL_EMC_ANDROID;
6057 GfxAction[newx][newy] = ACTION_GROWING;
6058 GfxDir[newx][newy] = diagonal_move_dir;
6059 ChangeDelay[newx][newy] = change_delay;
6061 graphic = el_act_dir2img(GfxElement[newx][newy],
6062 GfxAction[newx][newy], GfxDir[newx][newy]);
6064 DrawLevelGraphicAnimation(newx, newy, graphic);
6065 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6071 Feld[newx][newy] = EL_EMPTY;
6072 DrawLevelField(newx, newy);
6074 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6077 else if (!IS_FREE(newx, newy))
6080 if (IS_PLAYER(x, y))
6081 DrawPlayerField(x, y);
6083 DrawLevelField(x, y);
6089 else if (IS_CUSTOM_ELEMENT(element) &&
6090 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6092 int new_element = Feld[newx][newy];
6094 if (!IS_FREE(newx, newy))
6096 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6097 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6100 /* no element can dig solid indestructible elements */
6101 if (IS_INDESTRUCTIBLE(new_element) &&
6102 !IS_DIGGABLE(new_element) &&
6103 !IS_COLLECTIBLE(new_element))
6106 if (AmoebaNr[newx][newy] &&
6107 (new_element == EL_AMOEBA_FULL ||
6108 new_element == EL_BD_AMOEBA ||
6109 new_element == EL_AMOEBA_GROWING))
6111 AmoebaCnt[AmoebaNr[newx][newy]]--;
6112 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6115 if (IS_MOVING(newx, newy))
6116 RemoveMovingField(newx, newy);
6119 RemoveField(newx, newy);
6120 DrawLevelField(newx, newy);
6123 /* if digged element was about to explode, prevent the explosion */
6124 ExplodeField[newx][newy] = EX_TYPE_NONE;
6126 PlayLevelSoundAction(x, y, action);
6129 Store[newx][newy] = EL_EMPTY;
6131 /* this makes it possible to leave the removed element again */
6132 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6133 Store[newx][newy] = new_element;
6135 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6137 int move_leave_element = element_info[element].move_leave_element;
6139 /* this makes it possible to leave the removed element again */
6140 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6141 new_element : move_leave_element);
6145 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6147 RunnerVisit[x][y] = FrameCounter;
6148 PlayerVisit[x][y] /= 8; /* expire player visit path */
6151 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6153 if (!IS_FREE(newx, newy))
6155 if (IS_PLAYER(x, y))
6156 DrawPlayerField(x, y);
6158 DrawLevelField(x, y);
6164 boolean wanna_flame = !RND(10);
6165 int dx = newx - x, dy = newy - y;
6166 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6167 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6168 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6169 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6170 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6171 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6174 IS_CLASSIC_ENEMY(element1) ||
6175 IS_CLASSIC_ENEMY(element2)) &&
6176 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6177 element1 != EL_FLAMES && element2 != EL_FLAMES)
6179 ResetGfxAnimation(x, y);
6180 GfxAction[x][y] = ACTION_ATTACKING;
6182 if (IS_PLAYER(x, y))
6183 DrawPlayerField(x, y);
6185 DrawLevelField(x, y);
6187 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6189 MovDelay[x][y] = 50;
6193 RemoveField(newx, newy);
6195 Feld[newx][newy] = EL_FLAMES;
6196 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6199 RemoveField(newx1, newy1);
6201 Feld[newx1][newy1] = EL_FLAMES;
6203 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6206 RemoveField(newx2, newy2);
6208 Feld[newx2][newy2] = EL_FLAMES;
6215 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6216 Feld[newx][newy] == EL_DIAMOND)
6218 if (IS_MOVING(newx, newy))
6219 RemoveMovingField(newx, newy);
6222 Feld[newx][newy] = EL_EMPTY;
6223 DrawLevelField(newx, newy);
6226 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6228 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6229 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6231 if (AmoebaNr[newx][newy])
6233 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6234 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6235 Feld[newx][newy] == EL_BD_AMOEBA)
6236 AmoebaCnt[AmoebaNr[newx][newy]]--;
6241 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6243 RemoveMovingField(newx, newy);
6246 if (IS_MOVING(newx, newy))
6248 RemoveMovingField(newx, newy);
6253 Feld[newx][newy] = EL_EMPTY;
6254 DrawLevelField(newx, newy);
6257 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6259 else if ((element == EL_PACMAN || element == EL_MOLE)
6260 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6262 if (AmoebaNr[newx][newy])
6264 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6265 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6266 Feld[newx][newy] == EL_BD_AMOEBA)
6267 AmoebaCnt[AmoebaNr[newx][newy]]--;
6270 if (element == EL_MOLE)
6272 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6273 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6275 ResetGfxAnimation(x, y);
6276 GfxAction[x][y] = ACTION_DIGGING;
6277 DrawLevelField(x, y);
6279 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6281 return; /* wait for shrinking amoeba */
6283 else /* element == EL_PACMAN */
6285 Feld[newx][newy] = EL_EMPTY;
6286 DrawLevelField(newx, newy);
6287 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6290 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6291 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6292 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6294 /* wait for shrinking amoeba to completely disappear */
6297 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6299 /* object was running against a wall */
6304 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6305 if (move_pattern & MV_ANY_DIRECTION &&
6306 move_pattern == MovDir[x][y])
6308 int blocking_element =
6309 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6311 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6314 element = Feld[x][y]; /* element might have changed */
6318 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6319 DrawLevelElementAnimation(x, y, element);
6321 if (DONT_TOUCH(element))
6322 TestIfBadThingTouchesPlayer(x, y);
6327 InitMovingField(x, y, MovDir[x][y]);
6329 PlayLevelSoundAction(x, y, ACTION_MOVING);
6333 ContinueMoving(x, y);
6336 void ContinueMoving(int x, int y)
6338 int element = Feld[x][y];
6339 struct ElementInfo *ei = &element_info[element];
6340 int direction = MovDir[x][y];
6341 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6342 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6343 int newx = x + dx, newy = y + dy;
6344 int stored = Store[x][y];
6345 int stored_new = Store[newx][newy];
6346 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6347 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6348 boolean last_line = (newy == lev_fieldy - 1);
6350 MovPos[x][y] += getElementMoveStepsize(x, y);
6352 if (pushed_by_player) /* special case: moving object pushed by player */
6353 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6355 if (ABS(MovPos[x][y]) < TILEX)
6357 DrawLevelField(x, y);
6359 return; /* element is still moving */
6362 /* element reached destination field */
6364 Feld[x][y] = EL_EMPTY;
6365 Feld[newx][newy] = element;
6366 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6368 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6370 element = Feld[newx][newy] = EL_ACID;
6372 else if (element == EL_MOLE)
6374 Feld[x][y] = EL_SAND;
6376 DrawLevelFieldCrumbledSandNeighbours(x, y);
6378 else if (element == EL_QUICKSAND_FILLING)
6380 element = Feld[newx][newy] = get_next_element(element);
6381 Store[newx][newy] = Store[x][y];
6383 else if (element == EL_QUICKSAND_EMPTYING)
6385 Feld[x][y] = get_next_element(element);
6386 element = Feld[newx][newy] = Store[x][y];
6388 else if (element == EL_MAGIC_WALL_FILLING)
6390 element = Feld[newx][newy] = get_next_element(element);
6391 if (!game.magic_wall_active)
6392 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6393 Store[newx][newy] = Store[x][y];
6395 else if (element == EL_MAGIC_WALL_EMPTYING)
6397 Feld[x][y] = get_next_element(element);
6398 if (!game.magic_wall_active)
6399 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6400 element = Feld[newx][newy] = Store[x][y];
6402 #if USE_NEW_CUSTOM_VALUE
6403 InitField(newx, newy, FALSE);
6406 else if (element == EL_BD_MAGIC_WALL_FILLING)
6408 element = Feld[newx][newy] = get_next_element(element);
6409 if (!game.magic_wall_active)
6410 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6411 Store[newx][newy] = Store[x][y];
6413 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6415 Feld[x][y] = get_next_element(element);
6416 if (!game.magic_wall_active)
6417 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6418 element = Feld[newx][newy] = Store[x][y];
6420 #if USE_NEW_CUSTOM_VALUE
6421 InitField(newx, newy, FALSE);
6424 else if (element == EL_AMOEBA_DROPPING)
6426 Feld[x][y] = get_next_element(element);
6427 element = Feld[newx][newy] = Store[x][y];
6429 else if (element == EL_SOKOBAN_OBJECT)
6432 Feld[x][y] = Back[x][y];
6434 if (Back[newx][newy])
6435 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6437 Back[x][y] = Back[newx][newy] = 0;
6440 Store[x][y] = EL_EMPTY;
6445 MovDelay[newx][newy] = 0;
6447 if (CAN_CHANGE_OR_HAS_ACTION(element))
6449 /* copy element change control values to new field */
6450 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6451 ChangePage[newx][newy] = ChangePage[x][y];
6452 ChangeCount[newx][newy] = ChangeCount[x][y];
6453 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6456 #if USE_NEW_CUSTOM_VALUE
6457 CustomValue[newx][newy] = CustomValue[x][y];
6460 ChangeDelay[x][y] = 0;
6461 ChangePage[x][y] = -1;
6462 ChangeCount[x][y] = 0;
6463 ChangeEvent[x][y] = -1;
6465 #if USE_NEW_CUSTOM_VALUE
6466 CustomValue[x][y] = 0;
6469 /* copy animation control values to new field */
6470 GfxFrame[newx][newy] = GfxFrame[x][y];
6471 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6472 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6473 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6475 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6477 /* some elements can leave other elements behind after moving */
6479 if (ei->move_leave_element != EL_EMPTY &&
6480 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6481 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6483 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6484 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6485 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6488 int move_leave_element = ei->move_leave_element;
6492 /* this makes it possible to leave the removed element again */
6493 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6494 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6496 /* this makes it possible to leave the removed element again */
6497 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6498 move_leave_element = stored;
6501 /* this makes it possible to leave the removed element again */
6502 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6503 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6504 move_leave_element = stored;
6507 Feld[x][y] = move_leave_element;
6509 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6510 MovDir[x][y] = direction;
6512 InitField(x, y, FALSE);
6514 if (GFX_CRUMBLED(Feld[x][y]))
6515 DrawLevelFieldCrumbledSandNeighbours(x, y);
6517 if (ELEM_IS_PLAYER(move_leave_element))
6518 RelocatePlayer(x, y, move_leave_element);
6521 /* do this after checking for left-behind element */
6522 ResetGfxAnimation(x, y); /* reset animation values for old field */
6524 if (!CAN_MOVE(element) ||
6525 (CAN_FALL(element) && direction == MV_DOWN &&
6526 (element == EL_SPRING ||
6527 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6528 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6529 GfxDir[x][y] = MovDir[newx][newy] = 0;
6531 DrawLevelField(x, y);
6532 DrawLevelField(newx, newy);
6534 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6536 /* prevent pushed element from moving on in pushed direction */
6537 if (pushed_by_player && CAN_MOVE(element) &&
6538 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6539 !(element_info[element].move_pattern & direction))
6540 TurnRound(newx, newy);
6542 /* prevent elements on conveyor belt from moving on in last direction */
6543 if (pushed_by_conveyor && CAN_FALL(element) &&
6544 direction & MV_HORIZONTAL)
6545 MovDir[newx][newy] = 0;
6547 if (!pushed_by_player)
6549 int nextx = newx + dx, nexty = newy + dy;
6550 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6552 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6554 if (CAN_FALL(element) && direction == MV_DOWN)
6555 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6557 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6558 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6561 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6563 TestIfBadThingTouchesPlayer(newx, newy);
6564 TestIfBadThingTouchesFriend(newx, newy);
6566 if (!IS_CUSTOM_ELEMENT(element))
6567 TestIfBadThingTouchesOtherBadThing(newx, newy);
6569 else if (element == EL_PENGUIN)
6570 TestIfFriendTouchesBadThing(newx, newy);
6572 /* give the player one last chance (one more frame) to move away */
6573 if (CAN_FALL(element) && direction == MV_DOWN &&
6574 (last_line || (!IS_FREE(x, newy + 1) &&
6575 (!IS_PLAYER(x, newy + 1) ||
6576 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6579 if (pushed_by_player && !game.use_change_when_pushing_bug)
6581 int push_side = MV_DIR_OPPOSITE(direction);
6582 struct PlayerInfo *player = PLAYERINFO(x, y);
6584 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6585 player->index_bit, push_side);
6586 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6587 player->index_bit, push_side);
6590 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6591 MovDelay[newx][newy] = 1;
6593 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6595 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6598 if (ChangePage[newx][newy] != -1) /* delayed change */
6600 int page = ChangePage[newx][newy];
6601 struct ElementChangeInfo *change = &ei->change_page[page];
6603 ChangePage[newx][newy] = -1;
6605 if (change->can_change)
6607 if (ChangeElement(newx, newy, element, page))
6609 if (change->post_change_function)
6610 change->post_change_function(newx, newy);
6614 if (change->has_action)
6615 ExecuteCustomElementAction(newx, newy, element, page);
6619 TestIfElementHitsCustomElement(newx, newy, direction);
6620 TestIfPlayerTouchesCustomElement(newx, newy);
6621 TestIfElementTouchesCustomElement(newx, newy);
6623 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6624 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6625 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6626 MV_DIR_OPPOSITE(direction));
6629 int AmoebeNachbarNr(int ax, int ay)
6632 int element = Feld[ax][ay];
6634 static int xy[4][2] =
6642 for (i = 0; i < NUM_DIRECTIONS; i++)
6644 int x = ax + xy[i][0];
6645 int y = ay + xy[i][1];
6647 if (!IN_LEV_FIELD(x, y))
6650 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6651 group_nr = AmoebaNr[x][y];
6657 void AmoebenVereinigen(int ax, int ay)
6659 int i, x, y, xx, yy;
6660 int new_group_nr = AmoebaNr[ax][ay];
6661 static int xy[4][2] =
6669 if (new_group_nr == 0)
6672 for (i = 0; i < NUM_DIRECTIONS; i++)
6677 if (!IN_LEV_FIELD(x, y))
6680 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6681 Feld[x][y] == EL_BD_AMOEBA ||
6682 Feld[x][y] == EL_AMOEBA_DEAD) &&
6683 AmoebaNr[x][y] != new_group_nr)
6685 int old_group_nr = AmoebaNr[x][y];
6687 if (old_group_nr == 0)
6690 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6691 AmoebaCnt[old_group_nr] = 0;
6692 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6693 AmoebaCnt2[old_group_nr] = 0;
6695 SCAN_PLAYFIELD(xx, yy)
6697 if (AmoebaNr[xx][yy] == old_group_nr)
6698 AmoebaNr[xx][yy] = new_group_nr;
6704 void AmoebeUmwandeln(int ax, int ay)
6708 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6710 int group_nr = AmoebaNr[ax][ay];
6715 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6716 printf("AmoebeUmwandeln(): This should never happen!\n");
6721 SCAN_PLAYFIELD(x, y)
6723 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6726 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6730 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6731 SND_AMOEBA_TURNING_TO_GEM :
6732 SND_AMOEBA_TURNING_TO_ROCK));
6737 static int xy[4][2] =
6745 for (i = 0; i < NUM_DIRECTIONS; i++)
6750 if (!IN_LEV_FIELD(x, y))
6753 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6755 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6756 SND_AMOEBA_TURNING_TO_GEM :
6757 SND_AMOEBA_TURNING_TO_ROCK));
6764 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6767 int group_nr = AmoebaNr[ax][ay];
6768 boolean done = FALSE;
6773 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6774 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6779 SCAN_PLAYFIELD(x, y)
6781 if (AmoebaNr[x][y] == group_nr &&
6782 (Feld[x][y] == EL_AMOEBA_DEAD ||
6783 Feld[x][y] == EL_BD_AMOEBA ||
6784 Feld[x][y] == EL_AMOEBA_GROWING))
6787 Feld[x][y] = new_element;
6788 InitField(x, y, FALSE);
6789 DrawLevelField(x, y);
6795 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6796 SND_BD_AMOEBA_TURNING_TO_ROCK :
6797 SND_BD_AMOEBA_TURNING_TO_GEM));
6800 void AmoebeWaechst(int x, int y)
6802 static unsigned long sound_delay = 0;
6803 static unsigned long sound_delay_value = 0;
6805 if (!MovDelay[x][y]) /* start new growing cycle */
6809 if (DelayReached(&sound_delay, sound_delay_value))
6811 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6812 sound_delay_value = 30;
6816 if (MovDelay[x][y]) /* wait some time before growing bigger */
6819 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6821 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6822 6 - MovDelay[x][y]);
6824 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6827 if (!MovDelay[x][y])
6829 Feld[x][y] = Store[x][y];
6831 DrawLevelField(x, y);
6836 void AmoebaDisappearing(int x, int y)
6838 static unsigned long sound_delay = 0;
6839 static unsigned long sound_delay_value = 0;
6841 if (!MovDelay[x][y]) /* start new shrinking cycle */
6845 if (DelayReached(&sound_delay, sound_delay_value))
6846 sound_delay_value = 30;
6849 if (MovDelay[x][y]) /* wait some time before shrinking */
6852 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6854 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6855 6 - MovDelay[x][y]);
6857 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6860 if (!MovDelay[x][y])
6862 Feld[x][y] = EL_EMPTY;
6863 DrawLevelField(x, y);
6865 /* don't let mole enter this field in this cycle;
6866 (give priority to objects falling to this field from above) */
6872 void AmoebeAbleger(int ax, int ay)
6875 int element = Feld[ax][ay];
6876 int graphic = el2img(element);
6877 int newax = ax, neway = ay;
6878 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6879 static int xy[4][2] =
6887 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6889 Feld[ax][ay] = EL_AMOEBA_DEAD;
6890 DrawLevelField(ax, ay);
6894 if (IS_ANIMATED(graphic))
6895 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6897 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6898 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6900 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6903 if (MovDelay[ax][ay])
6907 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6910 int x = ax + xy[start][0];
6911 int y = ay + xy[start][1];
6913 if (!IN_LEV_FIELD(x, y))
6916 if (IS_FREE(x, y) ||
6917 CAN_GROW_INTO(Feld[x][y]) ||
6918 Feld[x][y] == EL_QUICKSAND_EMPTY)
6924 if (newax == ax && neway == ay)
6927 else /* normal or "filled" (BD style) amoeba */
6930 boolean waiting_for_player = FALSE;
6932 for (i = 0; i < NUM_DIRECTIONS; i++)
6934 int j = (start + i) % 4;
6935 int x = ax + xy[j][0];
6936 int y = ay + xy[j][1];
6938 if (!IN_LEV_FIELD(x, y))
6941 if (IS_FREE(x, y) ||
6942 CAN_GROW_INTO(Feld[x][y]) ||
6943 Feld[x][y] == EL_QUICKSAND_EMPTY)
6949 else if (IS_PLAYER(x, y))
6950 waiting_for_player = TRUE;
6953 if (newax == ax && neway == ay) /* amoeba cannot grow */
6955 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6957 Feld[ax][ay] = EL_AMOEBA_DEAD;
6958 DrawLevelField(ax, ay);
6959 AmoebaCnt[AmoebaNr[ax][ay]]--;
6961 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6963 if (element == EL_AMOEBA_FULL)
6964 AmoebeUmwandeln(ax, ay);
6965 else if (element == EL_BD_AMOEBA)
6966 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6971 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6973 /* amoeba gets larger by growing in some direction */
6975 int new_group_nr = AmoebaNr[ax][ay];
6978 if (new_group_nr == 0)
6980 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6981 printf("AmoebeAbleger(): This should never happen!\n");
6986 AmoebaNr[newax][neway] = new_group_nr;
6987 AmoebaCnt[new_group_nr]++;
6988 AmoebaCnt2[new_group_nr]++;
6990 /* if amoeba touches other amoeba(s) after growing, unify them */
6991 AmoebenVereinigen(newax, neway);
6993 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6995 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7001 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7002 (neway == lev_fieldy - 1 && newax != ax))
7004 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7005 Store[newax][neway] = element;
7007 else if (neway == ay || element == EL_EMC_DRIPPER)
7009 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7011 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7015 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7016 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7017 Store[ax][ay] = EL_AMOEBA_DROP;
7018 ContinueMoving(ax, ay);
7022 DrawLevelField(newax, neway);
7025 void Life(int ax, int ay)
7029 int element = Feld[ax][ay];
7030 int graphic = el2img(element);
7031 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7033 boolean changed = FALSE;
7035 if (IS_ANIMATED(graphic))
7036 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7041 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7042 MovDelay[ax][ay] = life_time;
7044 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7047 if (MovDelay[ax][ay])
7051 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7053 int xx = ax+x1, yy = ay+y1;
7056 if (!IN_LEV_FIELD(xx, yy))
7059 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7061 int x = xx+x2, y = yy+y2;
7063 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7066 if (((Feld[x][y] == element ||
7067 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7069 (IS_FREE(x, y) && Stop[x][y]))
7073 if (xx == ax && yy == ay) /* field in the middle */
7075 if (nachbarn < life_parameter[0] ||
7076 nachbarn > life_parameter[1])
7078 Feld[xx][yy] = EL_EMPTY;
7080 DrawLevelField(xx, yy);
7081 Stop[xx][yy] = TRUE;
7085 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7086 { /* free border field */
7087 if (nachbarn >= life_parameter[2] &&
7088 nachbarn <= life_parameter[3])
7090 Feld[xx][yy] = element;
7091 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7093 DrawLevelField(xx, yy);
7094 Stop[xx][yy] = TRUE;
7101 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7102 SND_GAME_OF_LIFE_GROWING);
7105 static void InitRobotWheel(int x, int y)
7107 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7110 static void RunRobotWheel(int x, int y)
7112 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7115 static void StopRobotWheel(int x, int y)
7117 if (ZX == x && ZY == y)
7121 static void InitTimegateWheel(int x, int y)
7123 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7126 static void RunTimegateWheel(int x, int y)
7128 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7131 static void InitMagicBallDelay(int x, int y)
7134 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7136 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7140 static void ActivateMagicBall(int bx, int by)
7144 if (level.ball_random)
7146 int pos_border = RND(8); /* select one of the eight border elements */
7147 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7148 int xx = pos_content % 3;
7149 int yy = pos_content / 3;
7154 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7155 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7159 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7161 int xx = x - bx + 1;
7162 int yy = y - by + 1;
7164 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7165 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7169 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7172 void CheckExit(int x, int y)
7174 if (local_player->gems_still_needed > 0 ||
7175 local_player->sokobanfields_still_needed > 0 ||
7176 local_player->lights_still_needed > 0)
7178 int element = Feld[x][y];
7179 int graphic = el2img(element);
7181 if (IS_ANIMATED(graphic))
7182 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7187 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7190 Feld[x][y] = EL_EXIT_OPENING;
7192 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7195 void CheckExitSP(int x, int y)
7197 if (local_player->gems_still_needed > 0)
7199 int element = Feld[x][y];
7200 int graphic = el2img(element);
7202 if (IS_ANIMATED(graphic))
7203 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7208 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7211 Feld[x][y] = EL_SP_EXIT_OPENING;
7213 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7216 static void CloseAllOpenTimegates()
7220 SCAN_PLAYFIELD(x, y)
7222 int element = Feld[x][y];
7224 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7226 Feld[x][y] = EL_TIMEGATE_CLOSING;
7228 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7233 void EdelsteinFunkeln(int x, int y)
7235 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7238 if (Feld[x][y] == EL_BD_DIAMOND)
7241 if (MovDelay[x][y] == 0) /* next animation frame */
7242 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7244 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7248 if (setup.direct_draw && MovDelay[x][y])
7249 SetDrawtoField(DRAW_BUFFERED);
7251 DrawLevelElementAnimation(x, y, Feld[x][y]);
7253 if (MovDelay[x][y] != 0)
7255 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7256 10 - MovDelay[x][y]);
7258 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7260 if (setup.direct_draw)
7264 dest_x = FX + SCREENX(x) * TILEX;
7265 dest_y = FY + SCREENY(y) * TILEY;
7267 BlitBitmap(drawto_field, window,
7268 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7269 SetDrawtoField(DRAW_DIRECT);
7275 void MauerWaechst(int x, int y)
7279 if (!MovDelay[x][y]) /* next animation frame */
7280 MovDelay[x][y] = 3 * delay;
7282 if (MovDelay[x][y]) /* wait some time before next frame */
7286 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7288 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7289 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7291 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7294 if (!MovDelay[x][y])
7296 if (MovDir[x][y] == MV_LEFT)
7298 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7299 DrawLevelField(x - 1, y);
7301 else if (MovDir[x][y] == MV_RIGHT)
7303 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7304 DrawLevelField(x + 1, y);
7306 else if (MovDir[x][y] == MV_UP)
7308 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7309 DrawLevelField(x, y - 1);
7313 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7314 DrawLevelField(x, y + 1);
7317 Feld[x][y] = Store[x][y];
7319 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7320 DrawLevelField(x, y);
7325 void MauerAbleger(int ax, int ay)
7327 int element = Feld[ax][ay];
7328 int graphic = el2img(element);
7329 boolean oben_frei = FALSE, unten_frei = FALSE;
7330 boolean links_frei = FALSE, rechts_frei = FALSE;
7331 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7332 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7333 boolean new_wall = FALSE;
7335 if (IS_ANIMATED(graphic))
7336 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7338 if (!MovDelay[ax][ay]) /* start building new wall */
7339 MovDelay[ax][ay] = 6;
7341 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7344 if (MovDelay[ax][ay])
7348 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7350 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7352 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7354 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7357 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7358 element == EL_EXPANDABLE_WALL_ANY)
7362 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7363 Store[ax][ay-1] = element;
7364 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7365 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7366 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7367 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7372 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7373 Store[ax][ay+1] = element;
7374 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7375 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7376 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7377 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7382 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7383 element == EL_EXPANDABLE_WALL_ANY ||
7384 element == EL_EXPANDABLE_WALL ||
7385 element == EL_BD_EXPANDABLE_WALL)
7389 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7390 Store[ax-1][ay] = element;
7391 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7392 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7393 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7394 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7400 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7401 Store[ax+1][ay] = element;
7402 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7403 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7404 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7405 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7410 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7411 DrawLevelField(ax, ay);
7413 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7415 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7416 unten_massiv = TRUE;
7417 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7418 links_massiv = TRUE;
7419 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7420 rechts_massiv = TRUE;
7422 if (((oben_massiv && unten_massiv) ||
7423 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7424 element == EL_EXPANDABLE_WALL) &&
7425 ((links_massiv && rechts_massiv) ||
7426 element == EL_EXPANDABLE_WALL_VERTICAL))
7427 Feld[ax][ay] = EL_WALL;
7430 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7433 void CheckForDragon(int x, int y)
7436 boolean dragon_found = FALSE;
7437 static int xy[4][2] =
7445 for (i = 0; i < NUM_DIRECTIONS; i++)
7447 for (j = 0; j < 4; j++)
7449 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7451 if (IN_LEV_FIELD(xx, yy) &&
7452 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7454 if (Feld[xx][yy] == EL_DRAGON)
7455 dragon_found = TRUE;
7464 for (i = 0; i < NUM_DIRECTIONS; i++)
7466 for (j = 0; j < 3; j++)
7468 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7470 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7472 Feld[xx][yy] = EL_EMPTY;
7473 DrawLevelField(xx, yy);
7482 static void InitBuggyBase(int x, int y)
7484 int element = Feld[x][y];
7485 int activating_delay = FRAMES_PER_SECOND / 4;
7488 (element == EL_SP_BUGGY_BASE ?
7489 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7490 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7492 element == EL_SP_BUGGY_BASE_ACTIVE ?
7493 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7496 static void WarnBuggyBase(int x, int y)
7499 static int xy[4][2] =
7507 for (i = 0; i < NUM_DIRECTIONS; i++)
7509 int xx = x + xy[i][0];
7510 int yy = y + xy[i][1];
7512 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7514 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7521 static void InitTrap(int x, int y)
7523 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7526 static void ActivateTrap(int x, int y)
7528 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7531 static void ChangeActiveTrap(int x, int y)
7533 int graphic = IMG_TRAP_ACTIVE;
7535 /* if new animation frame was drawn, correct crumbled sand border */
7536 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7537 DrawLevelFieldCrumbledSand(x, y);
7540 static int getSpecialActionElement(int element, int number, int base_element)
7542 return (element != EL_EMPTY ? element :
7543 number != -1 ? base_element + number - 1 :
7547 static int getModifiedActionNumber(int value_old, int operator, int operand,
7548 int value_min, int value_max)
7550 int value_new = (operator == CA_MODE_SET ? operand :
7551 operator == CA_MODE_ADD ? value_old + operand :
7552 operator == CA_MODE_SUBTRACT ? value_old - operand :
7553 operator == CA_MODE_MULTIPLY ? value_old * operand :
7554 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7555 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7558 return (value_new < value_min ? value_min :
7559 value_new > value_max ? value_max :
7563 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7565 struct ElementInfo *ei = &element_info[element];
7566 struct ElementChangeInfo *change = &ei->change_page[page];
7567 int target_element = change->target_element;
7568 int action_type = change->action_type;
7569 int action_mode = change->action_mode;
7570 int action_arg = change->action_arg;
7573 if (!change->has_action)
7576 /* ---------- determine action paramater values -------------------------- */
7578 int level_time_value =
7579 (level.time > 0 ? TimeLeft :
7582 int action_arg_element =
7583 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7584 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7585 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7588 int action_arg_direction =
7589 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7590 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7591 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7592 change->actual_trigger_side :
7593 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7594 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7597 int action_arg_number_min =
7598 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7601 int action_arg_number_max =
7602 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7603 action_type == CA_SET_LEVEL_GEMS ? 999 :
7604 action_type == CA_SET_LEVEL_TIME ? 9999 :
7605 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7606 action_type == CA_SET_CE_VALUE ? 9999 :
7607 action_type == CA_SET_CE_SCORE ? 9999 :
7610 int action_arg_number_reset =
7611 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7612 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7613 action_type == CA_SET_LEVEL_TIME ? level.time :
7614 action_type == CA_SET_LEVEL_SCORE ? 0 :
7615 #if USE_NEW_CUSTOM_VALUE
7616 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7618 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7620 action_type == CA_SET_CE_SCORE ? 0 :
7623 int action_arg_number =
7624 (action_arg <= CA_ARG_MAX ? action_arg :
7625 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7626 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7627 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7628 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7629 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7630 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7631 #if USE_NEW_CUSTOM_VALUE
7632 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7634 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7636 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7637 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7638 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7639 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7640 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7641 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7642 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7643 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7644 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7645 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7646 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7649 int action_arg_number_old =
7650 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7651 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7652 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7653 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7654 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7657 int action_arg_number_new =
7658 getModifiedActionNumber(action_arg_number_old,
7659 action_mode, action_arg_number,
7660 action_arg_number_min, action_arg_number_max);
7662 int trigger_player_bits =
7663 (change->actual_trigger_player >= EL_PLAYER_1 &&
7664 change->actual_trigger_player <= EL_PLAYER_4 ?
7665 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7668 int action_arg_player_bits =
7669 (action_arg >= CA_ARG_PLAYER_1 &&
7670 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7671 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7674 /* ---------- execute action -------------------------------------------- */
7683 /* ---------- level actions ------------------------------------------- */
7685 case CA_RESTART_LEVEL:
7687 game.restart_level = TRUE;
7692 case CA_SHOW_ENVELOPE:
7694 int element = getSpecialActionElement(action_arg_element,
7695 action_arg_number, EL_ENVELOPE_1);
7697 if (IS_ENVELOPE(element))
7698 local_player->show_envelope = element;
7703 case CA_SET_LEVEL_TIME:
7705 if (level.time > 0) /* only modify limited time value */
7707 TimeLeft = action_arg_number_new;
7709 DrawGameValue_Time(TimeLeft);
7711 if (!TimeLeft && setup.time_limit)
7712 for (i = 0; i < MAX_PLAYERS; i++)
7713 KillPlayer(&stored_player[i]);
7719 case CA_SET_LEVEL_SCORE:
7721 local_player->score = action_arg_number_new;
7723 DrawGameValue_Score(local_player->score);
7728 case CA_SET_LEVEL_GEMS:
7730 local_player->gems_still_needed = action_arg_number_new;
7732 DrawGameValue_Emeralds(local_player->gems_still_needed);
7737 #if !USE_PLAYER_GRAVITY
7738 case CA_SET_LEVEL_GRAVITY:
7740 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7741 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7742 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7748 case CA_SET_LEVEL_WIND:
7750 game.wind_direction = action_arg_direction;
7755 /* ---------- player actions ------------------------------------------ */
7757 case CA_MOVE_PLAYER:
7759 /* automatically move to the next field in specified direction */
7760 for (i = 0; i < MAX_PLAYERS; i++)
7761 if (trigger_player_bits & (1 << i))
7762 stored_player[i].programmed_action = action_arg_direction;
7767 case CA_EXIT_PLAYER:
7769 for (i = 0; i < MAX_PLAYERS; i++)
7770 if (action_arg_player_bits & (1 << i))
7771 PlayerWins(&stored_player[i]);
7776 case CA_KILL_PLAYER:
7778 for (i = 0; i < MAX_PLAYERS; i++)
7779 if (action_arg_player_bits & (1 << i))
7780 KillPlayer(&stored_player[i]);
7785 case CA_SET_PLAYER_KEYS:
7787 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7788 int element = getSpecialActionElement(action_arg_element,
7789 action_arg_number, EL_KEY_1);
7791 if (IS_KEY(element))
7793 for (i = 0; i < MAX_PLAYERS; i++)
7795 if (trigger_player_bits & (1 << i))
7797 stored_player[i].key[KEY_NR(element)] = key_state;
7799 DrawGameDoorValues();
7807 case CA_SET_PLAYER_SPEED:
7809 for (i = 0; i < MAX_PLAYERS; i++)
7811 if (trigger_player_bits & (1 << i))
7813 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7815 if (action_arg == CA_ARG_SPEED_FASTER &&
7816 stored_player[i].cannot_move)
7818 action_arg_number = STEPSIZE_VERY_SLOW;
7820 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7821 action_arg == CA_ARG_SPEED_FASTER)
7823 action_arg_number = 2;
7824 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7827 else if (action_arg == CA_ARG_NUMBER_RESET)
7829 action_arg_number = level.initial_player_stepsize[i];
7833 getModifiedActionNumber(move_stepsize,
7836 action_arg_number_min,
7837 action_arg_number_max);
7839 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7846 case CA_SET_PLAYER_SHIELD:
7848 for (i = 0; i < MAX_PLAYERS; i++)
7850 if (trigger_player_bits & (1 << i))
7852 if (action_arg == CA_ARG_SHIELD_OFF)
7854 stored_player[i].shield_normal_time_left = 0;
7855 stored_player[i].shield_deadly_time_left = 0;
7857 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7859 stored_player[i].shield_normal_time_left = 999999;
7861 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7863 stored_player[i].shield_normal_time_left = 999999;
7864 stored_player[i].shield_deadly_time_left = 999999;
7872 #if USE_PLAYER_GRAVITY
7873 case CA_SET_PLAYER_GRAVITY:
7875 for (i = 0; i < MAX_PLAYERS; i++)
7877 if (trigger_player_bits & (1 << i))
7879 stored_player[i].gravity =
7880 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7881 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7882 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7883 stored_player[i].gravity);
7891 case CA_SET_PLAYER_ARTWORK:
7893 for (i = 0; i < MAX_PLAYERS; i++)
7895 if (trigger_player_bits & (1 << i))
7897 int artwork_element = action_arg_element;
7899 if (action_arg == CA_ARG_ELEMENT_RESET)
7901 (level.use_artwork_element[i] ? level.artwork_element[i] :
7902 stored_player[i].element_nr);
7904 stored_player[i].artwork_element = artwork_element;
7906 SetPlayerWaiting(&stored_player[i], FALSE);
7908 /* set number of special actions for bored and sleeping animation */
7909 stored_player[i].num_special_action_bored =
7910 get_num_special_action(artwork_element,
7911 ACTION_BORING_1, ACTION_BORING_LAST);
7912 stored_player[i].num_special_action_sleeping =
7913 get_num_special_action(artwork_element,
7914 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7921 /* ---------- CE actions ---------------------------------------------- */
7923 case CA_SET_CE_VALUE:
7925 #if USE_NEW_CUSTOM_VALUE
7926 int last_ce_value = CustomValue[x][y];
7928 CustomValue[x][y] = action_arg_number_new;
7930 if (CustomValue[x][y] != last_ce_value)
7932 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
7933 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
7935 if (CustomValue[x][y] == 0)
7937 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7938 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7946 case CA_SET_CE_SCORE:
7948 #if USE_NEW_CUSTOM_VALUE
7949 int last_ce_score = ei->collect_score;
7951 ei->collect_score = action_arg_number_new;
7953 if (ei->collect_score != last_ce_score)
7955 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
7956 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
7958 if (ei->collect_score == 0)
7962 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
7963 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
7966 This is a very special case that seems to be a mixture between
7967 CheckElementChange() and CheckTriggeredElementChange(): while
7968 the first one only affects single elements that are triggered
7969 directly, the second one affects multiple elements in the playfield
7970 that are triggered indirectly by another element. This is a third
7971 case: Changing the CE score always affects multiple identical CEs,
7972 so every affected CE must be checked, not only the single CE for
7973 which the CE score was changed in the first place (as every instance
7974 of that CE shares the same CE score, and therefore also can change)!
7976 SCAN_PLAYFIELD(xx, yy)
7978 if (Feld[xx][yy] == element)
7979 CheckElementChange(xx, yy, element, EL_UNDEFINED,
7980 CE_SCORE_GETS_ZERO);
7989 /* ---------- engine actions ------------------------------------------ */
7991 case CA_SET_ENGINE_SCAN_MODE:
7993 InitPlayfieldScanMode(action_arg);
8003 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8005 int old_element = Feld[x][y];
8006 int new_element = get_element_from_group_element(element);
8007 int previous_move_direction = MovDir[x][y];
8008 #if USE_NEW_CUSTOM_VALUE
8009 int last_ce_value = CustomValue[x][y];
8011 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8012 boolean add_player_onto_element = (new_element_is_player &&
8013 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8014 /* this breaks SnakeBite when a snake is
8015 halfway through a door that closes */
8016 /* NOW FIXED AT LEVEL INIT IN files.c */
8017 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8019 IS_WALKABLE(old_element));
8022 /* check if element under the player changes from accessible to unaccessible
8023 (needed for special case of dropping element which then changes) */
8024 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8025 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8033 if (!add_player_onto_element)
8035 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8036 RemoveMovingField(x, y);
8040 Feld[x][y] = new_element;
8042 #if !USE_GFX_RESET_GFX_ANIMATION
8043 ResetGfxAnimation(x, y);
8044 ResetRandomAnimationValue(x, y);
8047 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8048 MovDir[x][y] = previous_move_direction;
8050 #if USE_NEW_CUSTOM_VALUE
8051 if (element_info[new_element].use_last_ce_value)
8052 CustomValue[x][y] = last_ce_value;
8055 InitField_WithBug1(x, y, FALSE);
8057 new_element = Feld[x][y]; /* element may have changed */
8059 #if USE_GFX_RESET_GFX_ANIMATION
8060 ResetGfxAnimation(x, y);
8061 ResetRandomAnimationValue(x, y);
8064 DrawLevelField(x, y);
8066 if (GFX_CRUMBLED(new_element))
8067 DrawLevelFieldCrumbledSandNeighbours(x, y);
8071 /* check if element under the player changes from accessible to unaccessible
8072 (needed for special case of dropping element which then changes) */
8073 /* (must be checked after creating new element for walkable group elements) */
8074 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8075 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8083 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8084 if (new_element_is_player)
8085 RelocatePlayer(x, y, new_element);
8088 ChangeCount[x][y]++; /* count number of changes in the same frame */
8090 TestIfBadThingTouchesPlayer(x, y);
8091 TestIfPlayerTouchesCustomElement(x, y);
8092 TestIfElementTouchesCustomElement(x, y);
8095 static void CreateField(int x, int y, int element)
8097 CreateFieldExt(x, y, element, FALSE);
8100 static void CreateElementFromChange(int x, int y, int element)
8102 element = GET_VALID_RUNTIME_ELEMENT(element);
8104 #if USE_STOP_CHANGED_ELEMENTS
8105 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8107 int old_element = Feld[x][y];
8109 /* prevent changed element from moving in same engine frame
8110 unless both old and new element can either fall or move */
8111 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8112 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8117 CreateFieldExt(x, y, element, TRUE);
8120 static boolean ChangeElement(int x, int y, int element, int page)
8122 struct ElementInfo *ei = &element_info[element];
8123 struct ElementChangeInfo *change = &ei->change_page[page];
8124 int ce_value = CustomValue[x][y];
8125 int ce_score = ei->collect_score;
8127 int old_element = Feld[x][y];
8129 /* always use default change event to prevent running into a loop */
8130 if (ChangeEvent[x][y] == -1)
8131 ChangeEvent[x][y] = CE_DELAY;
8133 if (ChangeEvent[x][y] == CE_DELAY)
8135 /* reset actual trigger element, trigger player and action element */
8136 change->actual_trigger_element = EL_EMPTY;
8137 change->actual_trigger_player = EL_PLAYER_1;
8138 change->actual_trigger_side = CH_SIDE_NONE;
8139 change->actual_trigger_ce_value = 0;
8140 change->actual_trigger_ce_score = 0;
8143 /* do not change elements more than a specified maximum number of changes */
8144 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8147 ChangeCount[x][y]++; /* count number of changes in the same frame */
8149 if (change->explode)
8156 if (change->use_target_content)
8158 boolean complete_replace = TRUE;
8159 boolean can_replace[3][3];
8162 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8165 boolean is_walkable;
8166 boolean is_diggable;
8167 boolean is_collectible;
8168 boolean is_removable;
8169 boolean is_destructible;
8170 int ex = x + xx - 1;
8171 int ey = y + yy - 1;
8172 int content_element = change->target_content.e[xx][yy];
8175 can_replace[xx][yy] = TRUE;
8177 if (ex == x && ey == y) /* do not check changing element itself */
8180 if (content_element == EL_EMPTY_SPACE)
8182 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8187 if (!IN_LEV_FIELD(ex, ey))
8189 can_replace[xx][yy] = FALSE;
8190 complete_replace = FALSE;
8197 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8198 e = MovingOrBlocked2Element(ex, ey);
8200 is_empty = (IS_FREE(ex, ey) ||
8201 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8203 is_walkable = (is_empty || IS_WALKABLE(e));
8204 is_diggable = (is_empty || IS_DIGGABLE(e));
8205 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8206 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8207 is_removable = (is_diggable || is_collectible);
8209 can_replace[xx][yy] =
8210 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8211 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8212 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8213 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8214 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8215 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8216 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8218 if (!can_replace[xx][yy])
8219 complete_replace = FALSE;
8222 if (!change->only_if_complete || complete_replace)
8224 boolean something_has_changed = FALSE;
8226 if (change->only_if_complete && change->use_random_replace &&
8227 RND(100) < change->random_percentage)
8230 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8232 int ex = x + xx - 1;
8233 int ey = y + yy - 1;
8234 int content_element;
8236 if (can_replace[xx][yy] && (!change->use_random_replace ||
8237 RND(100) < change->random_percentage))
8239 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8240 RemoveMovingField(ex, ey);
8242 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8244 content_element = change->target_content.e[xx][yy];
8245 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8246 ce_value, ce_score);
8248 CreateElementFromChange(ex, ey, target_element);
8250 something_has_changed = TRUE;
8252 /* for symmetry reasons, freeze newly created border elements */
8253 if (ex != x || ey != y)
8254 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8258 if (something_has_changed)
8260 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8261 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8267 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8268 ce_value, ce_score);
8270 if (element == EL_DIAGONAL_GROWING ||
8271 element == EL_DIAGONAL_SHRINKING)
8273 target_element = Store[x][y];
8275 Store[x][y] = EL_EMPTY;
8278 CreateElementFromChange(x, y, target_element);
8280 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8281 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8284 /* this uses direct change before indirect change */
8285 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8290 #if USE_NEW_DELAYED_ACTION
8292 static void HandleElementChange(int x, int y, int page)
8294 int element = MovingOrBlocked2Element(x, y);
8295 struct ElementInfo *ei = &element_info[element];
8296 struct ElementChangeInfo *change = &ei->change_page[page];
8299 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8300 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8303 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8304 x, y, element, element_info[element].token_name);
8305 printf("HandleElementChange(): This should never happen!\n");
8310 /* this can happen with classic bombs on walkable, changing elements */
8311 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8314 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8315 ChangeDelay[x][y] = 0;
8321 if (ChangeDelay[x][y] == 0) /* initialize element change */
8323 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8325 if (change->can_change)
8327 ResetGfxAnimation(x, y);
8328 ResetRandomAnimationValue(x, y);
8330 if (change->pre_change_function)
8331 change->pre_change_function(x, y);
8335 ChangeDelay[x][y]--;
8337 if (ChangeDelay[x][y] != 0) /* continue element change */
8339 if (change->can_change)
8341 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8343 if (IS_ANIMATED(graphic))
8344 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8346 if (change->change_function)
8347 change->change_function(x, y);
8350 else /* finish element change */
8352 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8354 page = ChangePage[x][y];
8355 ChangePage[x][y] = -1;
8357 change = &ei->change_page[page];
8360 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8362 ChangeDelay[x][y] = 1; /* try change after next move step */
8363 ChangePage[x][y] = page; /* remember page to use for change */
8368 if (change->can_change)
8370 if (ChangeElement(x, y, element, page))
8372 if (change->post_change_function)
8373 change->post_change_function(x, y);
8377 if (change->has_action)
8378 ExecuteCustomElementAction(x, y, element, page);
8384 static void HandleElementChange(int x, int y, int page)
8386 int element = MovingOrBlocked2Element(x, y);
8387 struct ElementInfo *ei = &element_info[element];
8388 struct ElementChangeInfo *change = &ei->change_page[page];
8391 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8394 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8395 x, y, element, element_info[element].token_name);
8396 printf("HandleElementChange(): This should never happen!\n");
8401 /* this can happen with classic bombs on walkable, changing elements */
8402 if (!CAN_CHANGE(element))
8405 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8406 ChangeDelay[x][y] = 0;
8412 if (ChangeDelay[x][y] == 0) /* initialize element change */
8414 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8416 ResetGfxAnimation(x, y);
8417 ResetRandomAnimationValue(x, y);
8419 if (change->pre_change_function)
8420 change->pre_change_function(x, y);
8423 ChangeDelay[x][y]--;
8425 if (ChangeDelay[x][y] != 0) /* continue element change */
8427 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8429 if (IS_ANIMATED(graphic))
8430 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8432 if (change->change_function)
8433 change->change_function(x, y);
8435 else /* finish element change */
8437 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8439 page = ChangePage[x][y];
8440 ChangePage[x][y] = -1;
8442 change = &ei->change_page[page];
8445 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8447 ChangeDelay[x][y] = 1; /* try change after next move step */
8448 ChangePage[x][y] = page; /* remember page to use for change */
8453 if (ChangeElement(x, y, element, page))
8455 if (change->post_change_function)
8456 change->post_change_function(x, y);
8463 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8464 int trigger_element,
8470 boolean change_done_any = FALSE;
8471 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8474 if (!(trigger_events[trigger_element][trigger_event]))
8477 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8479 int element = EL_CUSTOM_START + i;
8480 boolean change_done = FALSE;
8483 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8484 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8487 for (p = 0; p < element_info[element].num_change_pages; p++)
8489 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8491 if (change->can_change_or_has_action &&
8492 change->has_event[trigger_event] &&
8493 change->trigger_side & trigger_side &&
8494 change->trigger_player & trigger_player &&
8495 change->trigger_page & trigger_page_bits &&
8496 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8498 change->actual_trigger_element = trigger_element;
8499 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8500 change->actual_trigger_side = trigger_side;
8501 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8502 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8504 if ((change->can_change && !change_done) || change->has_action)
8508 SCAN_PLAYFIELD(x, y)
8510 if (Feld[x][y] == element)
8512 if (change->can_change && !change_done)
8514 ChangeDelay[x][y] = 1;
8515 ChangeEvent[x][y] = trigger_event;
8517 HandleElementChange(x, y, p);
8519 #if USE_NEW_DELAYED_ACTION
8520 else if (change->has_action)
8522 ExecuteCustomElementAction(x, y, element, p);
8523 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8526 if (change->has_action)
8528 ExecuteCustomElementAction(x, y, element, p);
8529 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8535 if (change->can_change)
8538 change_done_any = TRUE;
8545 return change_done_any;
8548 static boolean CheckElementChangeExt(int x, int y,
8550 int trigger_element,
8555 boolean change_done = FALSE;
8558 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8559 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8562 if (Feld[x][y] == EL_BLOCKED)
8564 Blocked2Moving(x, y, &x, &y);
8565 element = Feld[x][y];
8569 /* check if element has already changed */
8570 if (Feld[x][y] != element)
8573 /* check if element has already changed or is about to change after moving */
8574 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8575 Feld[x][y] != element) ||
8577 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8578 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8579 ChangePage[x][y] != -1)))
8583 for (p = 0; p < element_info[element].num_change_pages; p++)
8585 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8587 boolean check_trigger_element =
8588 (trigger_event == CE_TOUCHING_X ||
8589 trigger_event == CE_HITTING_X ||
8590 trigger_event == CE_HIT_BY_X);
8592 if (change->can_change_or_has_action &&
8593 change->has_event[trigger_event] &&
8594 change->trigger_side & trigger_side &&
8595 change->trigger_player & trigger_player &&
8596 (!check_trigger_element ||
8597 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8599 change->actual_trigger_element = trigger_element;
8600 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8601 change->actual_trigger_side = trigger_side;
8602 change->actual_trigger_ce_value = CustomValue[x][y];
8603 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8605 /* special case: trigger element not at (x,y) position for some events */
8606 if (check_trigger_element)
8618 { 0, 0 }, { 0, 0 }, { 0, 0 },
8622 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8623 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8625 change->actual_trigger_ce_value = CustomValue[xx][yy];
8626 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8629 if (change->can_change && !change_done)
8631 ChangeDelay[x][y] = 1;
8632 ChangeEvent[x][y] = trigger_event;
8634 HandleElementChange(x, y, p);
8638 #if USE_NEW_DELAYED_ACTION
8639 else if (change->has_action)
8641 ExecuteCustomElementAction(x, y, element, p);
8642 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8645 if (change->has_action)
8647 ExecuteCustomElementAction(x, y, element, p);
8648 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8657 static void PlayPlayerSound(struct PlayerInfo *player)
8659 int jx = player->jx, jy = player->jy;
8660 int sound_element = player->artwork_element;
8661 int last_action = player->last_action_waiting;
8662 int action = player->action_waiting;
8664 if (player->is_waiting)
8666 if (action != last_action)
8667 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8669 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8673 if (action != last_action)
8674 StopSound(element_info[sound_element].sound[last_action]);
8676 if (last_action == ACTION_SLEEPING)
8677 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8681 static void PlayAllPlayersSound()
8685 for (i = 0; i < MAX_PLAYERS; i++)
8686 if (stored_player[i].active)
8687 PlayPlayerSound(&stored_player[i]);
8690 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8692 boolean last_waiting = player->is_waiting;
8693 int move_dir = player->MovDir;
8695 player->dir_waiting = move_dir;
8696 player->last_action_waiting = player->action_waiting;
8700 if (!last_waiting) /* not waiting -> waiting */
8702 player->is_waiting = TRUE;
8704 player->frame_counter_bored =
8706 game.player_boring_delay_fixed +
8707 GetSimpleRandom(game.player_boring_delay_random);
8708 player->frame_counter_sleeping =
8710 game.player_sleeping_delay_fixed +
8711 GetSimpleRandom(game.player_sleeping_delay_random);
8713 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8716 if (game.player_sleeping_delay_fixed +
8717 game.player_sleeping_delay_random > 0 &&
8718 player->anim_delay_counter == 0 &&
8719 player->post_delay_counter == 0 &&
8720 FrameCounter >= player->frame_counter_sleeping)
8721 player->is_sleeping = TRUE;
8722 else if (game.player_boring_delay_fixed +
8723 game.player_boring_delay_random > 0 &&
8724 FrameCounter >= player->frame_counter_bored)
8725 player->is_bored = TRUE;
8727 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8728 player->is_bored ? ACTION_BORING :
8731 if (player->is_sleeping && player->use_murphy)
8733 /* special case for sleeping Murphy when leaning against non-free tile */
8735 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8736 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8737 !IS_MOVING(player->jx - 1, player->jy)))
8739 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8740 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8741 !IS_MOVING(player->jx + 1, player->jy)))
8742 move_dir = MV_RIGHT;
8744 player->is_sleeping = FALSE;
8746 player->dir_waiting = move_dir;
8749 if (player->is_sleeping)
8751 if (player->num_special_action_sleeping > 0)
8753 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8755 int last_special_action = player->special_action_sleeping;
8756 int num_special_action = player->num_special_action_sleeping;
8757 int special_action =
8758 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8759 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8760 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8761 last_special_action + 1 : ACTION_SLEEPING);
8762 int special_graphic =
8763 el_act_dir2img(player->artwork_element, special_action, move_dir);
8765 player->anim_delay_counter =
8766 graphic_info[special_graphic].anim_delay_fixed +
8767 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8768 player->post_delay_counter =
8769 graphic_info[special_graphic].post_delay_fixed +
8770 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8772 player->special_action_sleeping = special_action;
8775 if (player->anim_delay_counter > 0)
8777 player->action_waiting = player->special_action_sleeping;
8778 player->anim_delay_counter--;
8780 else if (player->post_delay_counter > 0)
8782 player->post_delay_counter--;
8786 else if (player->is_bored)
8788 if (player->num_special_action_bored > 0)
8790 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8792 int special_action =
8793 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8794 int special_graphic =
8795 el_act_dir2img(player->artwork_element, special_action, move_dir);
8797 player->anim_delay_counter =
8798 graphic_info[special_graphic].anim_delay_fixed +
8799 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8800 player->post_delay_counter =
8801 graphic_info[special_graphic].post_delay_fixed +
8802 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8804 player->special_action_bored = special_action;
8807 if (player->anim_delay_counter > 0)
8809 player->action_waiting = player->special_action_bored;
8810 player->anim_delay_counter--;
8812 else if (player->post_delay_counter > 0)
8814 player->post_delay_counter--;
8819 else if (last_waiting) /* waiting -> not waiting */
8821 player->is_waiting = FALSE;
8822 player->is_bored = FALSE;
8823 player->is_sleeping = FALSE;
8825 player->frame_counter_bored = -1;
8826 player->frame_counter_sleeping = -1;
8828 player->anim_delay_counter = 0;
8829 player->post_delay_counter = 0;
8831 player->dir_waiting = player->MovDir;
8832 player->action_waiting = ACTION_DEFAULT;
8834 player->special_action_bored = ACTION_DEFAULT;
8835 player->special_action_sleeping = ACTION_DEFAULT;
8839 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8841 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8842 int left = player_action & JOY_LEFT;
8843 int right = player_action & JOY_RIGHT;
8844 int up = player_action & JOY_UP;
8845 int down = player_action & JOY_DOWN;
8846 int button1 = player_action & JOY_BUTTON_1;
8847 int button2 = player_action & JOY_BUTTON_2;
8848 int dx = (left ? -1 : right ? 1 : 0);
8849 int dy = (up ? -1 : down ? 1 : 0);
8851 if (!player->active || tape.pausing)
8857 snapped = SnapField(player, dx, dy);
8861 dropped = DropElement(player);
8863 moved = MovePlayer(player, dx, dy);
8866 if (tape.single_step && tape.recording && !tape.pausing)
8868 if (button1 || (dropped && !moved))
8870 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8871 SnapField(player, 0, 0); /* stop snapping */
8875 SetPlayerWaiting(player, FALSE);
8877 return player_action;
8881 /* no actions for this player (no input at player's configured device) */
8883 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8884 SnapField(player, 0, 0);
8885 CheckGravityMovementWhenNotMoving(player);
8887 if (player->MovPos == 0)
8888 SetPlayerWaiting(player, TRUE);
8890 if (player->MovPos == 0) /* needed for tape.playing */
8891 player->is_moving = FALSE;
8893 player->is_dropping = FALSE;
8894 player->is_dropping_pressed = FALSE;
8895 player->drop_pressed_delay = 0;
8901 static void CheckLevelTime()
8905 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8907 if (level.native_em_level->lev->home == 0) /* all players at home */
8909 PlayerWins(local_player);
8911 AllPlayersGone = TRUE;
8913 level.native_em_level->lev->home = -1;
8916 if (level.native_em_level->ply[0]->alive == 0 &&
8917 level.native_em_level->ply[1]->alive == 0 &&
8918 level.native_em_level->ply[2]->alive == 0 &&
8919 level.native_em_level->ply[3]->alive == 0) /* all dead */
8920 AllPlayersGone = TRUE;
8923 if (TimeFrames >= FRAMES_PER_SECOND)
8928 for (i = 0; i < MAX_PLAYERS; i++)
8930 struct PlayerInfo *player = &stored_player[i];
8932 if (SHIELD_ON(player))
8934 player->shield_normal_time_left--;
8936 if (player->shield_deadly_time_left > 0)
8937 player->shield_deadly_time_left--;
8941 if (!local_player->LevelSolved && !level.use_step_counter)
8949 if (TimeLeft <= 10 && setup.time_limit)
8950 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
8952 DrawGameValue_Time(TimeLeft);
8954 if (!TimeLeft && setup.time_limit)
8956 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8957 level.native_em_level->lev->killed_out_of_time = TRUE;
8959 for (i = 0; i < MAX_PLAYERS; i++)
8960 KillPlayer(&stored_player[i]);
8963 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8964 DrawGameValue_Time(TimePlayed);
8966 level.native_em_level->lev->time =
8967 (level.time == 0 ? TimePlayed : TimeLeft);
8970 if (tape.recording || tape.playing)
8971 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8975 void AdvanceFrameAndPlayerCounters(int player_nr)
8979 /* advance frame counters (global frame counter and time frame counter) */
8983 /* advance player counters (counters for move delay, move animation etc.) */
8984 for (i = 0; i < MAX_PLAYERS; i++)
8986 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8987 int move_delay_value = stored_player[i].move_delay_value;
8988 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8990 if (!advance_player_counters) /* not all players may be affected */
8993 #if USE_NEW_PLAYER_ANIM
8994 if (move_frames == 0) /* less than one move per game frame */
8996 int stepsize = TILEX / move_delay_value;
8997 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8998 int count = (stored_player[i].is_moving ?
8999 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9001 if (count % delay == 0)
9006 stored_player[i].Frame += move_frames;
9008 if (stored_player[i].MovPos != 0)
9009 stored_player[i].StepFrame += move_frames;
9011 if (stored_player[i].move_delay > 0)
9012 stored_player[i].move_delay--;
9014 /* due to bugs in previous versions, counter must count up, not down */
9015 if (stored_player[i].push_delay != -1)
9016 stored_player[i].push_delay++;
9018 if (stored_player[i].drop_delay > 0)
9019 stored_player[i].drop_delay--;
9021 if (stored_player[i].is_dropping_pressed)
9022 stored_player[i].drop_pressed_delay++;
9026 void StartGameActions(boolean init_network_game, boolean record_tape,
9029 unsigned long new_random_seed = InitRND(random_seed);
9032 TapeStartRecording(new_random_seed);
9034 #if defined(NETWORK_AVALIABLE)
9035 if (init_network_game)
9037 SendToServer_StartPlaying();
9048 static unsigned long game_frame_delay = 0;
9049 unsigned long game_frame_delay_value;
9050 byte *recorded_player_action;
9051 byte summarized_player_action = 0;
9052 byte tape_action[MAX_PLAYERS];
9055 if (game.restart_level)
9056 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9058 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9060 if (level.native_em_level->lev->home == 0) /* all players at home */
9062 PlayerWins(local_player);
9064 AllPlayersGone = TRUE;
9066 level.native_em_level->lev->home = -1;
9069 if (level.native_em_level->ply[0]->alive == 0 &&
9070 level.native_em_level->ply[1]->alive == 0 &&
9071 level.native_em_level->ply[2]->alive == 0 &&
9072 level.native_em_level->ply[3]->alive == 0) /* all dead */
9073 AllPlayersGone = TRUE;
9076 if (local_player->LevelSolved)
9079 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9082 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9085 game_frame_delay_value =
9086 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9088 if (tape.playing && tape.warp_forward && !tape.pausing)
9089 game_frame_delay_value = 0;
9091 /* ---------- main game synchronization point ---------- */
9093 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9095 if (network_playing && !network_player_action_received)
9097 /* try to get network player actions in time */
9099 #if defined(NETWORK_AVALIABLE)
9100 /* last chance to get network player actions without main loop delay */
9104 /* game was quit by network peer */
9105 if (game_status != GAME_MODE_PLAYING)
9108 if (!network_player_action_received)
9109 return; /* failed to get network player actions in time */
9111 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9117 /* at this point we know that we really continue executing the game */
9119 network_player_action_received = FALSE;
9121 /* when playing tape, read previously recorded player input from tape data */
9122 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9125 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9130 if (tape.set_centered_player)
9132 game.centered_player_nr_next = tape.centered_player_nr_next;
9133 game.set_centered_player = TRUE;
9136 for (i = 0; i < MAX_PLAYERS; i++)
9138 summarized_player_action |= stored_player[i].action;
9140 if (!network_playing)
9141 stored_player[i].effective_action = stored_player[i].action;
9144 #if defined(NETWORK_AVALIABLE)
9145 if (network_playing)
9146 SendToServer_MovePlayer(summarized_player_action);
9149 if (!options.network && !setup.team_mode)
9150 local_player->effective_action = summarized_player_action;
9152 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9154 for (i = 0; i < MAX_PLAYERS; i++)
9155 stored_player[i].effective_action =
9156 (i == game.centered_player_nr ? summarized_player_action : 0);
9159 if (recorded_player_action != NULL)
9160 for (i = 0; i < MAX_PLAYERS; i++)
9161 stored_player[i].effective_action = recorded_player_action[i];
9163 for (i = 0; i < MAX_PLAYERS; i++)
9165 tape_action[i] = stored_player[i].effective_action;
9167 /* (this can only happen in the R'n'D game engine) */
9168 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9169 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9172 /* only record actions from input devices, but not programmed actions */
9174 TapeRecordAction(tape_action);
9176 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9178 GameActions_EM_Main();
9186 void GameActions_EM_Main()
9188 byte effective_action[MAX_PLAYERS];
9189 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9192 for (i = 0; i < MAX_PLAYERS; i++)
9193 effective_action[i] = stored_player[i].effective_action;
9195 GameActions_EM(effective_action, warp_mode);
9199 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9202 void GameActions_RND()
9204 int magic_wall_x = 0, magic_wall_y = 0;
9205 int i, x, y, element, graphic;
9207 InitPlayfieldScanModeVars();
9209 #if USE_ONE_MORE_CHANGE_PER_FRAME
9210 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9212 SCAN_PLAYFIELD(x, y)
9214 ChangeCount[x][y] = 0;
9215 ChangeEvent[x][y] = -1;
9220 if (game.set_centered_player)
9222 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9224 /* switching to "all players" only possible if all players fit to screen */
9225 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9227 game.centered_player_nr_next = game.centered_player_nr;
9228 game.set_centered_player = FALSE;
9231 /* do not switch focus to non-existing (or non-active) player */
9232 if (game.centered_player_nr_next >= 0 &&
9233 !stored_player[game.centered_player_nr_next].active)
9235 game.centered_player_nr_next = game.centered_player_nr;
9236 game.set_centered_player = FALSE;
9240 if (game.set_centered_player &&
9241 ScreenMovPos == 0) /* screen currently aligned at tile position */
9245 if (game.centered_player_nr_next == -1)
9247 setScreenCenteredToAllPlayers(&sx, &sy);
9251 sx = stored_player[game.centered_player_nr_next].jx;
9252 sy = stored_player[game.centered_player_nr_next].jy;
9255 game.centered_player_nr = game.centered_player_nr_next;
9256 game.set_centered_player = FALSE;
9258 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9259 DrawGameDoorValues();
9262 for (i = 0; i < MAX_PLAYERS; i++)
9264 int actual_player_action = stored_player[i].effective_action;
9267 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9268 - rnd_equinox_tetrachloride 048
9269 - rnd_equinox_tetrachloride_ii 096
9270 - rnd_emanuel_schmieg 002
9271 - doctor_sloan_ww 001, 020
9273 if (stored_player[i].MovPos == 0)
9274 CheckGravityMovement(&stored_player[i]);
9277 /* overwrite programmed action with tape action */
9278 if (stored_player[i].programmed_action)
9279 actual_player_action = stored_player[i].programmed_action;
9281 PlayerActions(&stored_player[i], actual_player_action);
9283 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9286 ScrollScreen(NULL, SCROLL_GO_ON);
9288 /* for backwards compatibility, the following code emulates a fixed bug that
9289 occured when pushing elements (causing elements that just made their last
9290 pushing step to already (if possible) make their first falling step in the
9291 same game frame, which is bad); this code is also needed to use the famous
9292 "spring push bug" which is used in older levels and might be wanted to be
9293 used also in newer levels, but in this case the buggy pushing code is only
9294 affecting the "spring" element and no other elements */
9296 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9298 for (i = 0; i < MAX_PLAYERS; i++)
9300 struct PlayerInfo *player = &stored_player[i];
9304 if (player->active && player->is_pushing && player->is_moving &&
9306 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9307 Feld[x][y] == EL_SPRING))
9309 ContinueMoving(x, y);
9311 /* continue moving after pushing (this is actually a bug) */
9312 if (!IS_MOVING(x, y))
9320 SCAN_PLAYFIELD(x, y)
9322 ChangeCount[x][y] = 0;
9323 ChangeEvent[x][y] = -1;
9325 /* this must be handled before main playfield loop */
9326 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9329 if (MovDelay[x][y] <= 0)
9333 #if USE_NEW_SNAP_DELAY
9334 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9337 if (MovDelay[x][y] <= 0)
9340 DrawLevelField(x, y);
9342 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9348 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9350 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9351 printf("GameActions(): This should never happen!\n");
9353 ChangePage[x][y] = -1;
9358 if (WasJustMoving[x][y] > 0)
9359 WasJustMoving[x][y]--;
9360 if (WasJustFalling[x][y] > 0)
9361 WasJustFalling[x][y]--;
9362 if (CheckCollision[x][y] > 0)
9363 CheckCollision[x][y]--;
9367 /* reset finished pushing action (not done in ContinueMoving() to allow
9368 continuous pushing animation for elements with zero push delay) */
9369 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9371 ResetGfxAnimation(x, y);
9372 DrawLevelField(x, y);
9376 if (IS_BLOCKED(x, y))
9380 Blocked2Moving(x, y, &oldx, &oldy);
9381 if (!IS_MOVING(oldx, oldy))
9383 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9384 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9385 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9386 printf("GameActions(): This should never happen!\n");
9392 SCAN_PLAYFIELD(x, y)
9394 element = Feld[x][y];
9395 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9397 ResetGfxFrame(x, y, TRUE);
9399 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9400 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9401 ResetRandomAnimationValue(x, y);
9403 SetRandomAnimationValue(x, y);
9405 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9407 if (IS_INACTIVE(element))
9409 if (IS_ANIMATED(graphic))
9410 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9415 /* this may take place after moving, so 'element' may have changed */
9416 if (IS_CHANGING(x, y) &&
9417 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9419 int page = element_info[element].event_page_nr[CE_DELAY];
9422 HandleElementChange(x, y, page);
9424 if (CAN_CHANGE(element))
9425 HandleElementChange(x, y, page);
9427 if (HAS_ACTION(element))
9428 ExecuteCustomElementAction(x, y, element, page);
9431 element = Feld[x][y];
9432 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9435 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9439 element = Feld[x][y];
9440 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9442 if (IS_ANIMATED(graphic) &&
9445 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9447 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9448 EdelsteinFunkeln(x, y);
9450 else if ((element == EL_ACID ||
9451 element == EL_EXIT_OPEN ||
9452 element == EL_SP_EXIT_OPEN ||
9453 element == EL_SP_TERMINAL ||
9454 element == EL_SP_TERMINAL_ACTIVE ||
9455 element == EL_EXTRA_TIME ||
9456 element == EL_SHIELD_NORMAL ||
9457 element == EL_SHIELD_DEADLY) &&
9458 IS_ANIMATED(graphic))
9459 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9460 else if (IS_MOVING(x, y))
9461 ContinueMoving(x, y);
9462 else if (IS_ACTIVE_BOMB(element))
9463 CheckDynamite(x, y);
9464 else if (element == EL_AMOEBA_GROWING)
9465 AmoebeWaechst(x, y);
9466 else if (element == EL_AMOEBA_SHRINKING)
9467 AmoebaDisappearing(x, y);
9469 #if !USE_NEW_AMOEBA_CODE
9470 else if (IS_AMOEBALIVE(element))
9471 AmoebeAbleger(x, y);
9474 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9476 else if (element == EL_EXIT_CLOSED)
9478 else if (element == EL_SP_EXIT_CLOSED)
9480 else if (element == EL_EXPANDABLE_WALL_GROWING)
9482 else if (element == EL_EXPANDABLE_WALL ||
9483 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9484 element == EL_EXPANDABLE_WALL_VERTICAL ||
9485 element == EL_EXPANDABLE_WALL_ANY ||
9486 element == EL_BD_EXPANDABLE_WALL)
9488 else if (element == EL_FLAMES)
9489 CheckForDragon(x, y);
9490 else if (element == EL_EXPLOSION)
9491 ; /* drawing of correct explosion animation is handled separately */
9492 else if (element == EL_ELEMENT_SNAPPING ||
9493 element == EL_DIAGONAL_SHRINKING ||
9494 element == EL_DIAGONAL_GROWING)
9496 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9498 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9500 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9501 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9503 if (IS_BELT_ACTIVE(element))
9504 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9506 if (game.magic_wall_active)
9508 int jx = local_player->jx, jy = local_player->jy;
9510 /* play the element sound at the position nearest to the player */
9511 if ((element == EL_MAGIC_WALL_FULL ||
9512 element == EL_MAGIC_WALL_ACTIVE ||
9513 element == EL_MAGIC_WALL_EMPTYING ||
9514 element == EL_BD_MAGIC_WALL_FULL ||
9515 element == EL_BD_MAGIC_WALL_ACTIVE ||
9516 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9517 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9525 #if USE_NEW_AMOEBA_CODE
9526 /* new experimental amoeba growth stuff */
9527 if (!(FrameCounter % 8))
9529 static unsigned long random = 1684108901;
9531 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9533 x = RND(lev_fieldx);
9534 y = RND(lev_fieldy);
9535 element = Feld[x][y];
9537 if (!IS_PLAYER(x,y) &&
9538 (element == EL_EMPTY ||
9539 CAN_GROW_INTO(element) ||
9540 element == EL_QUICKSAND_EMPTY ||
9541 element == EL_ACID_SPLASH_LEFT ||
9542 element == EL_ACID_SPLASH_RIGHT))
9544 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9545 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9546 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9547 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9548 Feld[x][y] = EL_AMOEBA_DROP;
9551 random = random * 129 + 1;
9557 if (game.explosions_delayed)
9560 game.explosions_delayed = FALSE;
9562 SCAN_PLAYFIELD(x, y)
9564 element = Feld[x][y];
9566 if (ExplodeField[x][y])
9567 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9568 else if (element == EL_EXPLOSION)
9569 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9571 ExplodeField[x][y] = EX_TYPE_NONE;
9574 game.explosions_delayed = TRUE;
9577 if (game.magic_wall_active)
9579 if (!(game.magic_wall_time_left % 4))
9581 int element = Feld[magic_wall_x][magic_wall_y];
9583 if (element == EL_BD_MAGIC_WALL_FULL ||
9584 element == EL_BD_MAGIC_WALL_ACTIVE ||
9585 element == EL_BD_MAGIC_WALL_EMPTYING)
9586 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9588 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9591 if (game.magic_wall_time_left > 0)
9593 game.magic_wall_time_left--;
9594 if (!game.magic_wall_time_left)
9596 SCAN_PLAYFIELD(x, y)
9598 element = Feld[x][y];
9600 if (element == EL_MAGIC_WALL_ACTIVE ||
9601 element == EL_MAGIC_WALL_FULL)
9603 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9604 DrawLevelField(x, y);
9606 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9607 element == EL_BD_MAGIC_WALL_FULL)
9609 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9610 DrawLevelField(x, y);
9614 game.magic_wall_active = FALSE;
9619 if (game.light_time_left > 0)
9621 game.light_time_left--;
9623 if (game.light_time_left == 0)
9624 RedrawAllLightSwitchesAndInvisibleElements();
9627 if (game.timegate_time_left > 0)
9629 game.timegate_time_left--;
9631 if (game.timegate_time_left == 0)
9632 CloseAllOpenTimegates();
9635 if (game.lenses_time_left > 0)
9637 game.lenses_time_left--;
9639 if (game.lenses_time_left == 0)
9640 RedrawAllInvisibleElementsForLenses();
9643 if (game.magnify_time_left > 0)
9645 game.magnify_time_left--;
9647 if (game.magnify_time_left == 0)
9648 RedrawAllInvisibleElementsForMagnifier();
9651 for (i = 0; i < MAX_PLAYERS; i++)
9653 struct PlayerInfo *player = &stored_player[i];
9655 if (SHIELD_ON(player))
9657 if (player->shield_deadly_time_left)
9658 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9659 else if (player->shield_normal_time_left)
9660 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9667 PlayAllPlayersSound();
9669 if (options.debug) /* calculate frames per second */
9671 static unsigned long fps_counter = 0;
9672 static int fps_frames = 0;
9673 unsigned long fps_delay_ms = Counter() - fps_counter;
9677 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9679 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9682 fps_counter = Counter();
9685 redraw_mask |= REDRAW_FPS;
9688 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9690 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9692 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9694 local_player->show_envelope = 0;
9697 /* use random number generator in every frame to make it less predictable */
9698 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9702 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9704 int min_x = x, min_y = y, max_x = x, max_y = y;
9707 for (i = 0; i < MAX_PLAYERS; i++)
9709 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9711 if (!stored_player[i].active || &stored_player[i] == player)
9714 min_x = MIN(min_x, jx);
9715 min_y = MIN(min_y, jy);
9716 max_x = MAX(max_x, jx);
9717 max_y = MAX(max_y, jy);
9720 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9723 static boolean AllPlayersInVisibleScreen()
9727 for (i = 0; i < MAX_PLAYERS; i++)
9729 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9731 if (!stored_player[i].active)
9734 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9741 void ScrollLevel(int dx, int dy)
9743 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9746 BlitBitmap(drawto_field, drawto_field,
9747 FX + TILEX * (dx == -1) - softscroll_offset,
9748 FY + TILEY * (dy == -1) - softscroll_offset,
9749 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9750 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9751 FX + TILEX * (dx == 1) - softscroll_offset,
9752 FY + TILEY * (dy == 1) - softscroll_offset);
9756 x = (dx == 1 ? BX1 : BX2);
9757 for (y = BY1; y <= BY2; y++)
9758 DrawScreenField(x, y);
9763 y = (dy == 1 ? BY1 : BY2);
9764 for (x = BX1; x <= BX2; x++)
9765 DrawScreenField(x, y);
9768 redraw_mask |= REDRAW_FIELD;
9771 static boolean canFallDown(struct PlayerInfo *player)
9773 int jx = player->jx, jy = player->jy;
9775 return (IN_LEV_FIELD(jx, jy + 1) &&
9776 (IS_FREE(jx, jy + 1) ||
9777 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9778 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9779 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9782 static boolean canPassField(int x, int y, int move_dir)
9784 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9785 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9786 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9789 int element = Feld[x][y];
9791 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9792 !CAN_MOVE(element) &&
9793 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9794 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9795 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9798 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9800 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9801 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9802 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9806 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9807 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9808 (IS_DIGGABLE(Feld[newx][newy]) ||
9809 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9810 canPassField(newx, newy, move_dir)));
9813 static void CheckGravityMovement(struct PlayerInfo *player)
9815 #if USE_PLAYER_GRAVITY
9816 if (player->gravity && !player->programmed_action)
9818 if (game.gravity && !player->programmed_action)
9821 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9822 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9823 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9824 int jx = player->jx, jy = player->jy;
9825 boolean player_is_moving_to_valid_field =
9826 (!player_is_snapping &&
9827 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9828 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9829 boolean player_can_fall_down = canFallDown(player);
9831 if (player_can_fall_down &&
9832 !player_is_moving_to_valid_field)
9833 player->programmed_action = MV_DOWN;
9837 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9839 return CheckGravityMovement(player);
9841 #if USE_PLAYER_GRAVITY
9842 if (player->gravity && !player->programmed_action)
9844 if (game.gravity && !player->programmed_action)
9847 int jx = player->jx, jy = player->jy;
9848 boolean field_under_player_is_free =
9849 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9850 boolean player_is_standing_on_valid_field =
9851 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9852 (IS_WALKABLE(Feld[jx][jy]) &&
9853 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9855 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9856 player->programmed_action = MV_DOWN;
9862 -----------------------------------------------------------------------------
9863 dx, dy: direction (non-diagonal) to try to move the player to
9864 real_dx, real_dy: direction as read from input device (can be diagonal)
9867 boolean MovePlayerOneStep(struct PlayerInfo *player,
9868 int dx, int dy, int real_dx, int real_dy)
9870 int jx = player->jx, jy = player->jy;
9871 int new_jx = jx + dx, new_jy = jy + dy;
9872 #if !USE_FIXED_DONT_RUN_INTO
9876 boolean player_can_move = !player->cannot_move;
9878 if (!player->active || (!dx && !dy))
9879 return MP_NO_ACTION;
9881 player->MovDir = (dx < 0 ? MV_LEFT :
9884 dy > 0 ? MV_DOWN : MV_NONE);
9886 if (!IN_LEV_FIELD(new_jx, new_jy))
9887 return MP_NO_ACTION;
9889 if (!player_can_move)
9891 if (player->MovPos == 0)
9893 player->is_moving = FALSE;
9894 player->is_digging = FALSE;
9895 player->is_collecting = FALSE;
9896 player->is_snapping = FALSE;
9897 player->is_pushing = FALSE;
9902 if (!options.network && game.centered_player_nr == -1 &&
9903 !AllPlayersInSight(player, new_jx, new_jy))
9904 return MP_NO_ACTION;
9906 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9907 return MP_NO_ACTION;
9910 #if !USE_FIXED_DONT_RUN_INTO
9911 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9913 /* (moved to DigField()) */
9914 if (player_can_move && DONT_RUN_INTO(element))
9916 if (element == EL_ACID && dx == 0 && dy == 1)
9918 SplashAcid(new_jx, new_jy);
9919 Feld[jx][jy] = EL_PLAYER_1;
9920 InitMovingField(jx, jy, MV_DOWN);
9921 Store[jx][jy] = EL_ACID;
9922 ContinueMoving(jx, jy);
9926 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9932 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9933 if (can_move != MP_MOVING)
9936 /* check if DigField() has caused relocation of the player */
9937 if (player->jx != jx || player->jy != jy)
9938 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9940 StorePlayer[jx][jy] = 0;
9941 player->last_jx = jx;
9942 player->last_jy = jy;
9943 player->jx = new_jx;
9944 player->jy = new_jy;
9945 StorePlayer[new_jx][new_jy] = player->element_nr;
9947 if (player->move_delay_value_next != -1)
9949 player->move_delay_value = player->move_delay_value_next;
9950 player->move_delay_value_next = -1;
9954 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9956 player->step_counter++;
9958 PlayerVisit[jx][jy] = FrameCounter;
9960 #if USE_UFAST_PLAYER_EXIT_BUGFIX
9961 player->is_moving = TRUE;
9965 /* should better be called in MovePlayer(), but this breaks some tapes */
9966 ScrollPlayer(player, SCROLL_INIT);
9972 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9974 int jx = player->jx, jy = player->jy;
9975 int old_jx = jx, old_jy = jy;
9976 int moved = MP_NO_ACTION;
9978 if (!player->active)
9983 if (player->MovPos == 0)
9985 player->is_moving = FALSE;
9986 player->is_digging = FALSE;
9987 player->is_collecting = FALSE;
9988 player->is_snapping = FALSE;
9989 player->is_pushing = FALSE;
9995 if (player->move_delay > 0)
9998 player->move_delay = -1; /* set to "uninitialized" value */
10000 /* store if player is automatically moved to next field */
10001 player->is_auto_moving = (player->programmed_action != MV_NONE);
10003 /* remove the last programmed player action */
10004 player->programmed_action = 0;
10006 if (player->MovPos)
10008 /* should only happen if pre-1.2 tape recordings are played */
10009 /* this is only for backward compatibility */
10011 int original_move_delay_value = player->move_delay_value;
10014 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10018 /* scroll remaining steps with finest movement resolution */
10019 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10021 while (player->MovPos)
10023 ScrollPlayer(player, SCROLL_GO_ON);
10024 ScrollScreen(NULL, SCROLL_GO_ON);
10026 AdvanceFrameAndPlayerCounters(player->index_nr);
10032 player->move_delay_value = original_move_delay_value;
10035 player->is_active = FALSE;
10037 if (player->last_move_dir & MV_HORIZONTAL)
10039 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10040 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10044 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10045 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10048 #if USE_FIXED_BORDER_RUNNING_GFX
10049 if (!moved && !player->is_active)
10051 player->is_moving = FALSE;
10052 player->is_digging = FALSE;
10053 player->is_collecting = FALSE;
10054 player->is_snapping = FALSE;
10055 player->is_pushing = FALSE;
10063 if (moved & MP_MOVING && !ScreenMovPos &&
10064 (player->index_nr == game.centered_player_nr ||
10065 game.centered_player_nr == -1))
10067 if (moved & MP_MOVING && !ScreenMovPos &&
10068 (player == local_player || !options.network))
10071 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10072 int offset = (setup.scroll_delay ? 3 : 0);
10074 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10076 /* actual player has left the screen -- scroll in that direction */
10077 if (jx != old_jx) /* player has moved horizontally */
10078 scroll_x += (jx - old_jx);
10079 else /* player has moved vertically */
10080 scroll_y += (jy - old_jy);
10084 if (jx != old_jx) /* player has moved horizontally */
10086 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10087 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10088 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10090 /* don't scroll over playfield boundaries */
10091 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10092 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10094 /* don't scroll more than one field at a time */
10095 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10097 /* don't scroll against the player's moving direction */
10098 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10099 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10100 scroll_x = old_scroll_x;
10102 else /* player has moved vertically */
10104 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10105 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10106 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10108 /* don't scroll over playfield boundaries */
10109 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10110 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10112 /* don't scroll more than one field at a time */
10113 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10115 /* don't scroll against the player's moving direction */
10116 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10117 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10118 scroll_y = old_scroll_y;
10122 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10125 if (!options.network && game.centered_player_nr == -1 &&
10126 !AllPlayersInVisibleScreen())
10128 scroll_x = old_scroll_x;
10129 scroll_y = old_scroll_y;
10133 if (!options.network && !AllPlayersInVisibleScreen())
10135 scroll_x = old_scroll_x;
10136 scroll_y = old_scroll_y;
10141 ScrollScreen(player, SCROLL_INIT);
10142 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10147 player->StepFrame = 0;
10149 if (moved & MP_MOVING)
10151 if (old_jx != jx && old_jy == jy)
10152 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10153 else if (old_jx == jx && old_jy != jy)
10154 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10156 DrawLevelField(jx, jy); /* for "crumbled sand" */
10158 player->last_move_dir = player->MovDir;
10159 player->is_moving = TRUE;
10160 player->is_snapping = FALSE;
10161 player->is_switching = FALSE;
10162 player->is_dropping = FALSE;
10163 player->is_dropping_pressed = FALSE;
10164 player->drop_pressed_delay = 0;
10167 /* should better be called here than above, but this breaks some tapes */
10168 ScrollPlayer(player, SCROLL_INIT);
10173 CheckGravityMovementWhenNotMoving(player);
10175 player->is_moving = FALSE;
10177 /* at this point, the player is allowed to move, but cannot move right now
10178 (e.g. because of something blocking the way) -- ensure that the player
10179 is also allowed to move in the next frame (in old versions before 3.1.1,
10180 the player was forced to wait again for eight frames before next try) */
10182 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10183 player->move_delay = 0; /* allow direct movement in the next frame */
10186 if (player->move_delay == -1) /* not yet initialized by DigField() */
10187 player->move_delay = player->move_delay_value;
10189 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10191 TestIfPlayerTouchesBadThing(jx, jy);
10192 TestIfPlayerTouchesCustomElement(jx, jy);
10195 if (!player->active)
10196 RemovePlayer(player);
10201 void ScrollPlayer(struct PlayerInfo *player, int mode)
10203 int jx = player->jx, jy = player->jy;
10204 int last_jx = player->last_jx, last_jy = player->last_jy;
10205 int move_stepsize = TILEX / player->move_delay_value;
10207 #if USE_NEW_PLAYER_SPEED
10208 if (!player->active)
10211 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10214 if (!player->active || player->MovPos == 0)
10218 if (mode == SCROLL_INIT)
10220 player->actual_frame_counter = FrameCounter;
10221 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10223 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10224 Feld[last_jx][last_jy] == EL_EMPTY)
10226 int last_field_block_delay = 0; /* start with no blocking at all */
10227 int block_delay_adjustment = player->block_delay_adjustment;
10229 /* if player blocks last field, add delay for exactly one move */
10230 if (player->block_last_field)
10232 last_field_block_delay += player->move_delay_value;
10234 /* when blocking enabled, prevent moving up despite gravity */
10235 #if USE_PLAYER_GRAVITY
10236 if (player->gravity && player->MovDir == MV_UP)
10237 block_delay_adjustment = -1;
10239 if (game.gravity && player->MovDir == MV_UP)
10240 block_delay_adjustment = -1;
10244 /* add block delay adjustment (also possible when not blocking) */
10245 last_field_block_delay += block_delay_adjustment;
10247 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10248 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10251 #if USE_NEW_PLAYER_SPEED
10252 if (player->MovPos != 0) /* player has not yet reached destination */
10258 else if (!FrameReached(&player->actual_frame_counter, 1))
10261 #if USE_NEW_PLAYER_SPEED
10262 if (player->MovPos != 0)
10264 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10265 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10267 /* before DrawPlayer() to draw correct player graphic for this case */
10268 if (player->MovPos == 0)
10269 CheckGravityMovement(player);
10272 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10273 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10275 /* before DrawPlayer() to draw correct player graphic for this case */
10276 if (player->MovPos == 0)
10277 CheckGravityMovement(player);
10280 if (player->MovPos == 0) /* player reached destination field */
10282 if (player->move_delay_reset_counter > 0)
10284 player->move_delay_reset_counter--;
10286 if (player->move_delay_reset_counter == 0)
10288 /* continue with normal speed after quickly moving through gate */
10289 HALVE_PLAYER_SPEED(player);
10291 /* be able to make the next move without delay */
10292 player->move_delay = 0;
10296 player->last_jx = jx;
10297 player->last_jy = jy;
10299 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10300 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10301 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10303 DrawPlayer(player); /* needed here only to cleanup last field */
10304 RemovePlayer(player);
10306 if (local_player->friends_still_needed == 0 ||
10307 IS_SP_ELEMENT(Feld[jx][jy]))
10308 PlayerWins(player);
10311 /* this breaks one level: "machine", level 000 */
10313 int move_direction = player->MovDir;
10314 int enter_side = MV_DIR_OPPOSITE(move_direction);
10315 int leave_side = move_direction;
10316 int old_jx = last_jx;
10317 int old_jy = last_jy;
10318 int old_element = Feld[old_jx][old_jy];
10319 int new_element = Feld[jx][jy];
10321 if (IS_CUSTOM_ELEMENT(old_element))
10322 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10324 player->index_bit, leave_side);
10326 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10327 CE_PLAYER_LEAVES_X,
10328 player->index_bit, leave_side);
10330 if (IS_CUSTOM_ELEMENT(new_element))
10331 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10332 player->index_bit, enter_side);
10334 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10335 CE_PLAYER_ENTERS_X,
10336 player->index_bit, enter_side);
10338 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10339 CE_MOVE_OF_X, move_direction);
10342 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10344 TestIfPlayerTouchesBadThing(jx, jy);
10345 TestIfPlayerTouchesCustomElement(jx, jy);
10347 /* needed because pushed element has not yet reached its destination,
10348 so it would trigger a change event at its previous field location */
10349 if (!player->is_pushing)
10350 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10352 if (!player->active)
10353 RemovePlayer(player);
10356 if (!local_player->LevelSolved && level.use_step_counter)
10366 if (TimeLeft <= 10 && setup.time_limit)
10367 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10369 DrawGameValue_Time(TimeLeft);
10371 if (!TimeLeft && setup.time_limit)
10372 for (i = 0; i < MAX_PLAYERS; i++)
10373 KillPlayer(&stored_player[i]);
10375 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10376 DrawGameValue_Time(TimePlayed);
10379 if (tape.single_step && tape.recording && !tape.pausing &&
10380 !player->programmed_action)
10381 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10385 void ScrollScreen(struct PlayerInfo *player, int mode)
10387 static unsigned long screen_frame_counter = 0;
10389 if (mode == SCROLL_INIT)
10391 /* set scrolling step size according to actual player's moving speed */
10392 ScrollStepSize = TILEX / player->move_delay_value;
10394 screen_frame_counter = FrameCounter;
10395 ScreenMovDir = player->MovDir;
10396 ScreenMovPos = player->MovPos;
10397 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10400 else if (!FrameReached(&screen_frame_counter, 1))
10405 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10406 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10407 redraw_mask |= REDRAW_FIELD;
10410 ScreenMovDir = MV_NONE;
10413 void TestIfPlayerTouchesCustomElement(int x, int y)
10415 static int xy[4][2] =
10422 static int trigger_sides[4][2] =
10424 /* center side border side */
10425 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10426 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10427 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10428 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10430 static int touch_dir[4] =
10432 MV_LEFT | MV_RIGHT,
10437 int center_element = Feld[x][y]; /* should always be non-moving! */
10440 for (i = 0; i < NUM_DIRECTIONS; i++)
10442 int xx = x + xy[i][0];
10443 int yy = y + xy[i][1];
10444 int center_side = trigger_sides[i][0];
10445 int border_side = trigger_sides[i][1];
10446 int border_element;
10448 if (!IN_LEV_FIELD(xx, yy))
10451 if (IS_PLAYER(x, y))
10453 struct PlayerInfo *player = PLAYERINFO(x, y);
10455 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10456 border_element = Feld[xx][yy]; /* may be moving! */
10457 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10458 border_element = Feld[xx][yy];
10459 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10460 border_element = MovingOrBlocked2Element(xx, yy);
10462 continue; /* center and border element do not touch */
10464 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10465 player->index_bit, border_side);
10466 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10467 CE_PLAYER_TOUCHES_X,
10468 player->index_bit, border_side);
10470 else if (IS_PLAYER(xx, yy))
10472 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10474 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10476 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10477 continue; /* center and border element do not touch */
10480 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10481 player->index_bit, center_side);
10482 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10483 CE_PLAYER_TOUCHES_X,
10484 player->index_bit, center_side);
10490 #if USE_ELEMENT_TOUCHING_BUGFIX
10492 void TestIfElementTouchesCustomElement(int x, int y)
10494 static int xy[4][2] =
10501 static int trigger_sides[4][2] =
10503 /* center side border side */
10504 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10505 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10506 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10507 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10509 static int touch_dir[4] =
10511 MV_LEFT | MV_RIGHT,
10516 boolean change_center_element = FALSE;
10517 int center_element = Feld[x][y]; /* should always be non-moving! */
10518 int border_element_old[NUM_DIRECTIONS];
10521 for (i = 0; i < NUM_DIRECTIONS; i++)
10523 int xx = x + xy[i][0];
10524 int yy = y + xy[i][1];
10525 int border_element;
10527 border_element_old[i] = -1;
10529 if (!IN_LEV_FIELD(xx, yy))
10532 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10533 border_element = Feld[xx][yy]; /* may be moving! */
10534 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10535 border_element = Feld[xx][yy];
10536 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10537 border_element = MovingOrBlocked2Element(xx, yy);
10539 continue; /* center and border element do not touch */
10541 border_element_old[i] = border_element;
10544 for (i = 0; i < NUM_DIRECTIONS; i++)
10546 int xx = x + xy[i][0];
10547 int yy = y + xy[i][1];
10548 int center_side = trigger_sides[i][0];
10549 int border_element = border_element_old[i];
10551 if (border_element == -1)
10554 /* check for change of border element */
10555 CheckElementChangeBySide(xx, yy, border_element, center_element,
10556 CE_TOUCHING_X, center_side);
10559 for (i = 0; i < NUM_DIRECTIONS; i++)
10561 int border_side = trigger_sides[i][1];
10562 int border_element = border_element_old[i];
10564 if (border_element == -1)
10567 /* check for change of center element (but change it only once) */
10568 if (!change_center_element)
10569 change_center_element =
10570 CheckElementChangeBySide(x, y, center_element, border_element,
10571 CE_TOUCHING_X, border_side);
10577 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10579 static int xy[4][2] =
10586 static int trigger_sides[4][2] =
10588 /* center side border side */
10589 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10590 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10591 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10592 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10594 static int touch_dir[4] =
10596 MV_LEFT | MV_RIGHT,
10601 boolean change_center_element = FALSE;
10602 int center_element = Feld[x][y]; /* should always be non-moving! */
10605 for (i = 0; i < NUM_DIRECTIONS; i++)
10607 int xx = x + xy[i][0];
10608 int yy = y + xy[i][1];
10609 int center_side = trigger_sides[i][0];
10610 int border_side = trigger_sides[i][1];
10611 int border_element;
10613 if (!IN_LEV_FIELD(xx, yy))
10616 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10617 border_element = Feld[xx][yy]; /* may be moving! */
10618 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10619 border_element = Feld[xx][yy];
10620 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10621 border_element = MovingOrBlocked2Element(xx, yy);
10623 continue; /* center and border element do not touch */
10625 /* check for change of center element (but change it only once) */
10626 if (!change_center_element)
10627 change_center_element =
10628 CheckElementChangeBySide(x, y, center_element, border_element,
10629 CE_TOUCHING_X, border_side);
10631 /* check for change of border element */
10632 CheckElementChangeBySide(xx, yy, border_element, center_element,
10633 CE_TOUCHING_X, center_side);
10639 void TestIfElementHitsCustomElement(int x, int y, int direction)
10641 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10642 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10643 int hitx = x + dx, hity = y + dy;
10644 int hitting_element = Feld[x][y];
10645 int touched_element;
10647 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10650 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10651 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10653 if (IN_LEV_FIELD(hitx, hity))
10655 int opposite_direction = MV_DIR_OPPOSITE(direction);
10656 int hitting_side = direction;
10657 int touched_side = opposite_direction;
10658 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10659 MovDir[hitx][hity] != direction ||
10660 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10666 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10667 CE_HITTING_X, touched_side);
10669 CheckElementChangeBySide(hitx, hity, touched_element,
10670 hitting_element, CE_HIT_BY_X, hitting_side);
10672 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10673 CE_HIT_BY_SOMETHING, opposite_direction);
10677 /* "hitting something" is also true when hitting the playfield border */
10678 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10679 CE_HITTING_SOMETHING, direction);
10683 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10685 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10686 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10687 int hitx = x + dx, hity = y + dy;
10688 int hitting_element = Feld[x][y];
10689 int touched_element;
10691 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10692 !IS_FREE(hitx, hity) &&
10693 (!IS_MOVING(hitx, hity) ||
10694 MovDir[hitx][hity] != direction ||
10695 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10698 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10702 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10706 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10707 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10709 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10710 EP_CAN_SMASH_EVERYTHING, direction);
10712 if (IN_LEV_FIELD(hitx, hity))
10714 int opposite_direction = MV_DIR_OPPOSITE(direction);
10715 int hitting_side = direction;
10716 int touched_side = opposite_direction;
10718 int touched_element = MovingOrBlocked2Element(hitx, hity);
10721 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10722 MovDir[hitx][hity] != direction ||
10723 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10732 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10733 CE_SMASHED_BY_SOMETHING, opposite_direction);
10735 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10736 CE_OTHER_IS_SMASHING, touched_side);
10738 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10739 CE_OTHER_GETS_SMASHED, hitting_side);
10745 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10747 int i, kill_x = -1, kill_y = -1;
10749 int bad_element = -1;
10750 static int test_xy[4][2] =
10757 static int test_dir[4] =
10765 for (i = 0; i < NUM_DIRECTIONS; i++)
10767 int test_x, test_y, test_move_dir, test_element;
10769 test_x = good_x + test_xy[i][0];
10770 test_y = good_y + test_xy[i][1];
10772 if (!IN_LEV_FIELD(test_x, test_y))
10776 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10778 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10780 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10781 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10783 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10784 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10788 bad_element = test_element;
10794 if (kill_x != -1 || kill_y != -1)
10796 if (IS_PLAYER(good_x, good_y))
10798 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10800 if (player->shield_deadly_time_left > 0 &&
10801 !IS_INDESTRUCTIBLE(bad_element))
10802 Bang(kill_x, kill_y);
10803 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10804 KillPlayer(player);
10807 Bang(good_x, good_y);
10811 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10813 int i, kill_x = -1, kill_y = -1;
10814 int bad_element = Feld[bad_x][bad_y];
10815 static int test_xy[4][2] =
10822 static int touch_dir[4] =
10824 MV_LEFT | MV_RIGHT,
10829 static int test_dir[4] =
10837 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10840 for (i = 0; i < NUM_DIRECTIONS; i++)
10842 int test_x, test_y, test_move_dir, test_element;
10844 test_x = bad_x + test_xy[i][0];
10845 test_y = bad_y + test_xy[i][1];
10846 if (!IN_LEV_FIELD(test_x, test_y))
10850 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10852 test_element = Feld[test_x][test_y];
10854 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10855 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10857 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10858 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10860 /* good thing is player or penguin that does not move away */
10861 if (IS_PLAYER(test_x, test_y))
10863 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10865 if (bad_element == EL_ROBOT && player->is_moving)
10866 continue; /* robot does not kill player if he is moving */
10868 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10870 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10871 continue; /* center and border element do not touch */
10878 else if (test_element == EL_PENGUIN)
10887 if (kill_x != -1 || kill_y != -1)
10889 if (IS_PLAYER(kill_x, kill_y))
10891 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10893 if (player->shield_deadly_time_left > 0 &&
10894 !IS_INDESTRUCTIBLE(bad_element))
10895 Bang(bad_x, bad_y);
10896 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10897 KillPlayer(player);
10900 Bang(kill_x, kill_y);
10904 void TestIfPlayerTouchesBadThing(int x, int y)
10906 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10909 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10911 TestIfGoodThingHitsBadThing(x, y, move_dir);
10914 void TestIfBadThingTouchesPlayer(int x, int y)
10916 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10919 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10921 TestIfBadThingHitsGoodThing(x, y, move_dir);
10924 void TestIfFriendTouchesBadThing(int x, int y)
10926 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10929 void TestIfBadThingTouchesFriend(int x, int y)
10931 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10934 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10936 int i, kill_x = bad_x, kill_y = bad_y;
10937 static int xy[4][2] =
10945 for (i = 0; i < NUM_DIRECTIONS; i++)
10949 x = bad_x + xy[i][0];
10950 y = bad_y + xy[i][1];
10951 if (!IN_LEV_FIELD(x, y))
10954 element = Feld[x][y];
10955 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10956 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10964 if (kill_x != bad_x || kill_y != bad_y)
10965 Bang(bad_x, bad_y);
10968 void KillPlayer(struct PlayerInfo *player)
10970 int jx = player->jx, jy = player->jy;
10972 if (!player->active)
10975 /* remove accessible field at the player's position */
10976 Feld[jx][jy] = EL_EMPTY;
10978 /* deactivate shield (else Bang()/Explode() would not work right) */
10979 player->shield_normal_time_left = 0;
10980 player->shield_deadly_time_left = 0;
10983 BuryPlayer(player);
10986 static void KillPlayerUnlessEnemyProtected(int x, int y)
10988 if (!PLAYER_ENEMY_PROTECTED(x, y))
10989 KillPlayer(PLAYERINFO(x, y));
10992 static void KillPlayerUnlessExplosionProtected(int x, int y)
10994 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10995 KillPlayer(PLAYERINFO(x, y));
10998 void BuryPlayer(struct PlayerInfo *player)
11000 int jx = player->jx, jy = player->jy;
11002 if (!player->active)
11005 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11006 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11008 player->GameOver = TRUE;
11009 RemovePlayer(player);
11012 void RemovePlayer(struct PlayerInfo *player)
11014 int jx = player->jx, jy = player->jy;
11015 int i, found = FALSE;
11017 player->present = FALSE;
11018 player->active = FALSE;
11020 if (!ExplodeField[jx][jy])
11021 StorePlayer[jx][jy] = 0;
11023 if (player->is_moving)
11024 DrawLevelField(player->last_jx, player->last_jy);
11026 for (i = 0; i < MAX_PLAYERS; i++)
11027 if (stored_player[i].active)
11031 AllPlayersGone = TRUE;
11037 #if USE_NEW_SNAP_DELAY
11038 static void setFieldForSnapping(int x, int y, int element, int direction)
11040 struct ElementInfo *ei = &element_info[element];
11041 int direction_bit = MV_DIR_TO_BIT(direction);
11042 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11043 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11044 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11046 Feld[x][y] = EL_ELEMENT_SNAPPING;
11047 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11049 ResetGfxAnimation(x, y);
11051 GfxElement[x][y] = element;
11052 GfxAction[x][y] = action;
11053 GfxDir[x][y] = direction;
11054 GfxFrame[x][y] = -1;
11059 =============================================================================
11060 checkDiagonalPushing()
11061 -----------------------------------------------------------------------------
11062 check if diagonal input device direction results in pushing of object
11063 (by checking if the alternative direction is walkable, diggable, ...)
11064 =============================================================================
11067 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11068 int x, int y, int real_dx, int real_dy)
11070 int jx, jy, dx, dy, xx, yy;
11072 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11075 /* diagonal direction: check alternative direction */
11080 xx = jx + (dx == 0 ? real_dx : 0);
11081 yy = jy + (dy == 0 ? real_dy : 0);
11083 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11087 =============================================================================
11089 -----------------------------------------------------------------------------
11090 x, y: field next to player (non-diagonal) to try to dig to
11091 real_dx, real_dy: direction as read from input device (can be diagonal)
11092 =============================================================================
11095 int DigField(struct PlayerInfo *player,
11096 int oldx, int oldy, int x, int y,
11097 int real_dx, int real_dy, int mode)
11099 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11100 boolean player_was_pushing = player->is_pushing;
11101 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11102 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11103 int jx = oldx, jy = oldy;
11104 int dx = x - jx, dy = y - jy;
11105 int nextx = x + dx, nexty = y + dy;
11106 int move_direction = (dx == -1 ? MV_LEFT :
11107 dx == +1 ? MV_RIGHT :
11109 dy == +1 ? MV_DOWN : MV_NONE);
11110 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11111 int dig_side = MV_DIR_OPPOSITE(move_direction);
11112 int old_element = Feld[jx][jy];
11113 #if USE_FIXED_DONT_RUN_INTO
11114 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11120 if (is_player) /* function can also be called by EL_PENGUIN */
11122 if (player->MovPos == 0)
11124 player->is_digging = FALSE;
11125 player->is_collecting = FALSE;
11128 if (player->MovPos == 0) /* last pushing move finished */
11129 player->is_pushing = FALSE;
11131 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11133 player->is_switching = FALSE;
11134 player->push_delay = -1;
11136 return MP_NO_ACTION;
11140 #if !USE_FIXED_DONT_RUN_INTO
11141 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11142 return MP_NO_ACTION;
11145 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11146 old_element = Back[jx][jy];
11148 /* in case of element dropped at player position, check background */
11149 else if (Back[jx][jy] != EL_EMPTY &&
11150 game.engine_version >= VERSION_IDENT(2,2,0,0))
11151 old_element = Back[jx][jy];
11153 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11154 return MP_NO_ACTION; /* field has no opening in this direction */
11156 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11157 return MP_NO_ACTION; /* field has no opening in this direction */
11159 #if USE_FIXED_DONT_RUN_INTO
11160 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11164 Feld[jx][jy] = player->artwork_element;
11165 InitMovingField(jx, jy, MV_DOWN);
11166 Store[jx][jy] = EL_ACID;
11167 ContinueMoving(jx, jy);
11168 BuryPlayer(player);
11170 return MP_DONT_RUN_INTO;
11174 #if USE_FIXED_DONT_RUN_INTO
11175 if (player_can_move && DONT_RUN_INTO(element))
11177 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11179 return MP_DONT_RUN_INTO;
11183 #if USE_FIXED_DONT_RUN_INTO
11184 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11185 return MP_NO_ACTION;
11188 #if !USE_FIXED_DONT_RUN_INTO
11189 element = Feld[x][y];
11192 collect_count = element_info[element].collect_count_initial;
11194 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11195 return MP_NO_ACTION;
11197 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11198 player_can_move = player_can_move_or_snap;
11200 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11201 game.engine_version >= VERSION_IDENT(2,2,0,0))
11203 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11204 player->index_bit, dig_side);
11205 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11206 player->index_bit, dig_side);
11208 if (Feld[x][y] != element) /* field changed by snapping */
11211 return MP_NO_ACTION;
11214 #if USE_PLAYER_GRAVITY
11215 if (player->gravity && is_player && !player->is_auto_moving &&
11216 canFallDown(player) && move_direction != MV_DOWN &&
11217 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11218 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11220 if (game.gravity && is_player && !player->is_auto_moving &&
11221 canFallDown(player) && move_direction != MV_DOWN &&
11222 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11223 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11226 if (player_can_move &&
11227 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11229 int sound_element = SND_ELEMENT(element);
11230 int sound_action = ACTION_WALKING;
11232 if (IS_RND_GATE(element))
11234 if (!player->key[RND_GATE_NR(element)])
11235 return MP_NO_ACTION;
11237 else if (IS_RND_GATE_GRAY(element))
11239 if (!player->key[RND_GATE_GRAY_NR(element)])
11240 return MP_NO_ACTION;
11242 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11244 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11245 return MP_NO_ACTION;
11247 else if (element == EL_EXIT_OPEN ||
11248 element == EL_SP_EXIT_OPEN ||
11249 element == EL_SP_EXIT_OPENING)
11251 sound_action = ACTION_PASSING; /* player is passing exit */
11253 else if (element == EL_EMPTY)
11255 sound_action = ACTION_MOVING; /* nothing to walk on */
11258 /* play sound from background or player, whatever is available */
11259 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11260 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11262 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11264 else if (player_can_move &&
11265 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11267 if (!ACCESS_FROM(element, opposite_direction))
11268 return MP_NO_ACTION; /* field not accessible from this direction */
11270 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11271 return MP_NO_ACTION;
11273 if (IS_EM_GATE(element))
11275 if (!player->key[EM_GATE_NR(element)])
11276 return MP_NO_ACTION;
11278 else if (IS_EM_GATE_GRAY(element))
11280 if (!player->key[EM_GATE_GRAY_NR(element)])
11281 return MP_NO_ACTION;
11283 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11285 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11286 return MP_NO_ACTION;
11288 else if (IS_EMC_GATE(element))
11290 if (!player->key[EMC_GATE_NR(element)])
11291 return MP_NO_ACTION;
11293 else if (IS_EMC_GATE_GRAY(element))
11295 if (!player->key[EMC_GATE_GRAY_NR(element)])
11296 return MP_NO_ACTION;
11298 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11300 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11301 return MP_NO_ACTION;
11303 else if (IS_SP_PORT(element))
11305 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11306 element == EL_SP_GRAVITY_PORT_RIGHT ||
11307 element == EL_SP_GRAVITY_PORT_UP ||
11308 element == EL_SP_GRAVITY_PORT_DOWN)
11309 #if USE_PLAYER_GRAVITY
11310 player->gravity = !player->gravity;
11312 game.gravity = !game.gravity;
11314 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11315 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11316 element == EL_SP_GRAVITY_ON_PORT_UP ||
11317 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11318 #if USE_PLAYER_GRAVITY
11319 player->gravity = TRUE;
11321 game.gravity = TRUE;
11323 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11324 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11325 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11326 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11327 #if USE_PLAYER_GRAVITY
11328 player->gravity = FALSE;
11330 game.gravity = FALSE;
11334 /* automatically move to the next field with double speed */
11335 player->programmed_action = move_direction;
11337 if (player->move_delay_reset_counter == 0)
11339 player->move_delay_reset_counter = 2; /* two double speed steps */
11341 DOUBLE_PLAYER_SPEED(player);
11344 PlayLevelSoundAction(x, y, ACTION_PASSING);
11346 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11350 if (mode != DF_SNAP)
11352 GfxElement[x][y] = GFX_ELEMENT(element);
11353 player->is_digging = TRUE;
11356 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11358 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11359 player->index_bit, dig_side);
11361 if (mode == DF_SNAP)
11363 #if USE_NEW_SNAP_DELAY
11364 if (level.block_snap_field)
11365 setFieldForSnapping(x, y, element, move_direction);
11367 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11369 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11372 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11373 player->index_bit, dig_side);
11376 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11380 if (is_player && mode != DF_SNAP)
11382 GfxElement[x][y] = element;
11383 player->is_collecting = TRUE;
11386 if (element == EL_SPEED_PILL)
11388 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11390 else if (element == EL_EXTRA_TIME && level.time > 0)
11392 TimeLeft += level.extra_time;
11393 DrawGameValue_Time(TimeLeft);
11395 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11397 player->shield_normal_time_left += level.shield_normal_time;
11398 if (element == EL_SHIELD_DEADLY)
11399 player->shield_deadly_time_left += level.shield_deadly_time;
11401 else if (element == EL_DYNAMITE ||
11402 element == EL_EM_DYNAMITE ||
11403 element == EL_SP_DISK_RED)
11405 if (player->inventory_size < MAX_INVENTORY_SIZE)
11406 player->inventory_element[player->inventory_size++] = element;
11408 DrawGameDoorValues();
11410 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11412 player->dynabomb_count++;
11413 player->dynabombs_left++;
11415 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11417 player->dynabomb_size++;
11419 else if (element == EL_DYNABOMB_INCREASE_POWER)
11421 player->dynabomb_xl = TRUE;
11423 else if (IS_KEY(element))
11425 player->key[KEY_NR(element)] = TRUE;
11427 DrawGameDoorValues();
11429 else if (IS_ENVELOPE(element))
11431 player->show_envelope = element;
11433 else if (element == EL_EMC_LENSES)
11435 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11437 RedrawAllInvisibleElementsForLenses();
11439 else if (element == EL_EMC_MAGNIFIER)
11441 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11443 RedrawAllInvisibleElementsForMagnifier();
11445 else if (IS_DROPPABLE(element) ||
11446 IS_THROWABLE(element)) /* can be collected and dropped */
11450 if (collect_count == 0)
11451 player->inventory_infinite_element = element;
11453 for (i = 0; i < collect_count; i++)
11454 if (player->inventory_size < MAX_INVENTORY_SIZE)
11455 player->inventory_element[player->inventory_size++] = element;
11457 DrawGameDoorValues();
11459 else if (collect_count > 0)
11461 local_player->gems_still_needed -= collect_count;
11462 if (local_player->gems_still_needed < 0)
11463 local_player->gems_still_needed = 0;
11465 DrawGameValue_Emeralds(local_player->gems_still_needed);
11468 RaiseScoreElement(element);
11469 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11472 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11473 player->index_bit, dig_side);
11475 if (mode == DF_SNAP)
11477 #if USE_NEW_SNAP_DELAY
11478 if (level.block_snap_field)
11479 setFieldForSnapping(x, y, element, move_direction);
11481 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11483 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11486 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11487 player->index_bit, dig_side);
11490 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11492 if (mode == DF_SNAP && element != EL_BD_ROCK)
11493 return MP_NO_ACTION;
11495 if (CAN_FALL(element) && dy)
11496 return MP_NO_ACTION;
11498 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11499 !(element == EL_SPRING && level.use_spring_bug))
11500 return MP_NO_ACTION;
11502 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11503 ((move_direction & MV_VERTICAL &&
11504 ((element_info[element].move_pattern & MV_LEFT &&
11505 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11506 (element_info[element].move_pattern & MV_RIGHT &&
11507 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11508 (move_direction & MV_HORIZONTAL &&
11509 ((element_info[element].move_pattern & MV_UP &&
11510 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11511 (element_info[element].move_pattern & MV_DOWN &&
11512 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11513 return MP_NO_ACTION;
11515 /* do not push elements already moving away faster than player */
11516 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11517 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11518 return MP_NO_ACTION;
11520 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11522 if (player->push_delay_value == -1 || !player_was_pushing)
11523 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11525 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11527 if (player->push_delay_value == -1)
11528 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11530 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11532 if (!player->is_pushing)
11533 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11536 player->is_pushing = TRUE;
11537 player->is_active = TRUE;
11539 if (!(IN_LEV_FIELD(nextx, nexty) &&
11540 (IS_FREE(nextx, nexty) ||
11541 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11542 IS_SB_ELEMENT(element)))))
11543 return MP_NO_ACTION;
11545 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11546 return MP_NO_ACTION;
11548 if (player->push_delay == -1) /* new pushing; restart delay */
11549 player->push_delay = 0;
11551 if (player->push_delay < player->push_delay_value &&
11552 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11553 element != EL_SPRING && element != EL_BALLOON)
11555 /* make sure that there is no move delay before next try to push */
11556 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11557 player->move_delay = 0;
11559 return MP_NO_ACTION;
11562 if (IS_SB_ELEMENT(element))
11564 if (element == EL_SOKOBAN_FIELD_FULL)
11566 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11567 local_player->sokobanfields_still_needed++;
11570 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11572 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11573 local_player->sokobanfields_still_needed--;
11576 Feld[x][y] = EL_SOKOBAN_OBJECT;
11578 if (Back[x][y] == Back[nextx][nexty])
11579 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11580 else if (Back[x][y] != 0)
11581 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11584 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11587 if (local_player->sokobanfields_still_needed == 0 &&
11588 game.emulation == EMU_SOKOBAN)
11590 PlayerWins(player);
11592 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11596 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11598 InitMovingField(x, y, move_direction);
11599 GfxAction[x][y] = ACTION_PUSHING;
11601 if (mode == DF_SNAP)
11602 ContinueMoving(x, y);
11604 MovPos[x][y] = (dx != 0 ? dx : dy);
11606 Pushed[x][y] = TRUE;
11607 Pushed[nextx][nexty] = TRUE;
11609 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11610 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11612 player->push_delay_value = -1; /* get new value later */
11614 /* check for element change _after_ element has been pushed */
11615 if (game.use_change_when_pushing_bug)
11617 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11618 player->index_bit, dig_side);
11619 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11620 player->index_bit, dig_side);
11623 else if (IS_SWITCHABLE(element))
11625 if (PLAYER_SWITCHING(player, x, y))
11627 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11628 player->index_bit, dig_side);
11633 player->is_switching = TRUE;
11634 player->switch_x = x;
11635 player->switch_y = y;
11637 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11639 if (element == EL_ROBOT_WHEEL)
11641 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11645 DrawLevelField(x, y);
11647 else if (element == EL_SP_TERMINAL)
11651 SCAN_PLAYFIELD(xx, yy)
11653 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11655 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11656 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11659 else if (IS_BELT_SWITCH(element))
11661 ToggleBeltSwitch(x, y);
11663 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11664 element == EL_SWITCHGATE_SWITCH_DOWN)
11666 ToggleSwitchgateSwitch(x, y);
11668 else if (element == EL_LIGHT_SWITCH ||
11669 element == EL_LIGHT_SWITCH_ACTIVE)
11671 ToggleLightSwitch(x, y);
11673 else if (element == EL_TIMEGATE_SWITCH)
11675 ActivateTimegateSwitch(x, y);
11677 else if (element == EL_BALLOON_SWITCH_LEFT ||
11678 element == EL_BALLOON_SWITCH_RIGHT ||
11679 element == EL_BALLOON_SWITCH_UP ||
11680 element == EL_BALLOON_SWITCH_DOWN ||
11681 element == EL_BALLOON_SWITCH_NONE ||
11682 element == EL_BALLOON_SWITCH_ANY)
11684 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11685 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11686 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11687 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11688 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11691 else if (element == EL_LAMP)
11693 Feld[x][y] = EL_LAMP_ACTIVE;
11694 local_player->lights_still_needed--;
11696 ResetGfxAnimation(x, y);
11697 DrawLevelField(x, y);
11699 else if (element == EL_TIME_ORB_FULL)
11701 Feld[x][y] = EL_TIME_ORB_EMPTY;
11703 if (level.time > 0 || level.use_time_orb_bug)
11705 TimeLeft += level.time_orb_time;
11706 DrawGameValue_Time(TimeLeft);
11709 ResetGfxAnimation(x, y);
11710 DrawLevelField(x, y);
11712 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11713 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11717 game.ball_state = !game.ball_state;
11719 SCAN_PLAYFIELD(xx, yy)
11721 int e = Feld[xx][yy];
11723 if (game.ball_state)
11725 if (e == EL_EMC_MAGIC_BALL)
11726 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11727 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11728 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11732 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11733 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11734 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11735 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11740 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11741 player->index_bit, dig_side);
11743 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11744 player->index_bit, dig_side);
11746 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11747 player->index_bit, dig_side);
11753 if (!PLAYER_SWITCHING(player, x, y))
11755 player->is_switching = TRUE;
11756 player->switch_x = x;
11757 player->switch_y = y;
11759 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11760 player->index_bit, dig_side);
11761 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11762 player->index_bit, dig_side);
11764 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11765 player->index_bit, dig_side);
11766 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11767 player->index_bit, dig_side);
11770 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11771 player->index_bit, dig_side);
11772 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11773 player->index_bit, dig_side);
11775 return MP_NO_ACTION;
11778 player->push_delay = -1;
11780 if (is_player) /* function can also be called by EL_PENGUIN */
11782 if (Feld[x][y] != element) /* really digged/collected something */
11784 player->is_collecting = !player->is_digging;
11785 player->is_active = TRUE;
11792 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11794 int jx = player->jx, jy = player->jy;
11795 int x = jx + dx, y = jy + dy;
11796 int snap_direction = (dx == -1 ? MV_LEFT :
11797 dx == +1 ? MV_RIGHT :
11799 dy == +1 ? MV_DOWN : MV_NONE);
11800 boolean can_continue_snapping = (level.continuous_snapping &&
11801 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11803 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11806 if (!player->active || !IN_LEV_FIELD(x, y))
11814 if (player->MovPos == 0)
11815 player->is_pushing = FALSE;
11817 player->is_snapping = FALSE;
11819 if (player->MovPos == 0)
11821 player->is_moving = FALSE;
11822 player->is_digging = FALSE;
11823 player->is_collecting = FALSE;
11829 #if USE_NEW_CONTINUOUS_SNAPPING
11830 /* prevent snapping with already pressed snap key when not allowed */
11831 if (player->is_snapping && !can_continue_snapping)
11834 if (player->is_snapping)
11838 player->MovDir = snap_direction;
11840 if (player->MovPos == 0)
11842 player->is_moving = FALSE;
11843 player->is_digging = FALSE;
11844 player->is_collecting = FALSE;
11847 player->is_dropping = FALSE;
11848 player->is_dropping_pressed = FALSE;
11849 player->drop_pressed_delay = 0;
11851 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11854 player->is_snapping = TRUE;
11855 player->is_active = TRUE;
11857 if (player->MovPos == 0)
11859 player->is_moving = FALSE;
11860 player->is_digging = FALSE;
11861 player->is_collecting = FALSE;
11864 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11865 DrawLevelField(player->last_jx, player->last_jy);
11867 DrawLevelField(x, y);
11872 boolean DropElement(struct PlayerInfo *player)
11874 int old_element, new_element;
11875 int dropx = player->jx, dropy = player->jy;
11876 int drop_direction = player->MovDir;
11877 int drop_side = drop_direction;
11878 int drop_element = (player->inventory_size > 0 ?
11879 player->inventory_element[player->inventory_size - 1] :
11880 player->inventory_infinite_element != EL_UNDEFINED ?
11881 player->inventory_infinite_element :
11882 player->dynabombs_left > 0 ?
11883 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11886 player->is_dropping_pressed = TRUE;
11888 /* do not drop an element on top of another element; when holding drop key
11889 pressed without moving, dropped element must move away before the next
11890 element can be dropped (this is especially important if the next element
11891 is dynamite, which can be placed on background for historical reasons) */
11892 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11895 if (IS_THROWABLE(drop_element))
11897 dropx += GET_DX_FROM_DIR(drop_direction);
11898 dropy += GET_DY_FROM_DIR(drop_direction);
11900 if (!IN_LEV_FIELD(dropx, dropy))
11904 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11905 new_element = drop_element; /* default: no change when dropping */
11907 /* check if player is active, not moving and ready to drop */
11908 if (!player->active || player->MovPos || player->drop_delay > 0)
11911 /* check if player has anything that can be dropped */
11912 if (new_element == EL_UNDEFINED)
11915 /* check if drop key was pressed long enough for EM style dynamite */
11916 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11919 /* check if anything can be dropped at the current position */
11920 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11923 /* collected custom elements can only be dropped on empty fields */
11924 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11927 if (old_element != EL_EMPTY)
11928 Back[dropx][dropy] = old_element; /* store old element on this field */
11930 ResetGfxAnimation(dropx, dropy);
11931 ResetRandomAnimationValue(dropx, dropy);
11933 if (player->inventory_size > 0 ||
11934 player->inventory_infinite_element != EL_UNDEFINED)
11936 if (player->inventory_size > 0)
11938 player->inventory_size--;
11940 DrawGameDoorValues();
11942 if (new_element == EL_DYNAMITE)
11943 new_element = EL_DYNAMITE_ACTIVE;
11944 else if (new_element == EL_EM_DYNAMITE)
11945 new_element = EL_EM_DYNAMITE_ACTIVE;
11946 else if (new_element == EL_SP_DISK_RED)
11947 new_element = EL_SP_DISK_RED_ACTIVE;
11950 Feld[dropx][dropy] = new_element;
11952 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11953 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11954 el2img(Feld[dropx][dropy]), 0);
11956 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11958 /* needed if previous element just changed to "empty" in the last frame */
11959 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11961 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11962 player->index_bit, drop_side);
11963 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11965 player->index_bit, drop_side);
11967 TestIfElementTouchesCustomElement(dropx, dropy);
11969 else /* player is dropping a dyna bomb */
11971 player->dynabombs_left--;
11973 Feld[dropx][dropy] = new_element;
11975 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11976 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11977 el2img(Feld[dropx][dropy]), 0);
11979 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11982 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11983 InitField_WithBug1(dropx, dropy, FALSE);
11985 new_element = Feld[dropx][dropy]; /* element might have changed */
11987 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11988 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11990 int move_direction, nextx, nexty;
11992 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11993 MovDir[dropx][dropy] = drop_direction;
11995 move_direction = MovDir[dropx][dropy];
11996 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11997 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11999 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12000 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12003 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12004 player->is_dropping = TRUE;
12006 player->drop_pressed_delay = 0;
12007 player->is_dropping_pressed = FALSE;
12009 player->drop_x = dropx;
12010 player->drop_y = dropy;
12015 /* ------------------------------------------------------------------------- */
12016 /* game sound playing functions */
12017 /* ------------------------------------------------------------------------- */
12019 static int *loop_sound_frame = NULL;
12020 static int *loop_sound_volume = NULL;
12022 void InitPlayLevelSound()
12024 int num_sounds = getSoundListSize();
12026 checked_free(loop_sound_frame);
12027 checked_free(loop_sound_volume);
12029 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12030 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12033 static void PlayLevelSound(int x, int y, int nr)
12035 int sx = SCREENX(x), sy = SCREENY(y);
12036 int volume, stereo_position;
12037 int max_distance = 8;
12038 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12040 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12041 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12044 if (!IN_LEV_FIELD(x, y) ||
12045 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12046 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12049 volume = SOUND_MAX_VOLUME;
12051 if (!IN_SCR_FIELD(sx, sy))
12053 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12054 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12056 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12059 stereo_position = (SOUND_MAX_LEFT +
12060 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12061 (SCR_FIELDX + 2 * max_distance));
12063 if (IS_LOOP_SOUND(nr))
12065 /* This assures that quieter loop sounds do not overwrite louder ones,
12066 while restarting sound volume comparison with each new game frame. */
12068 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12071 loop_sound_volume[nr] = volume;
12072 loop_sound_frame[nr] = FrameCounter;
12075 PlaySoundExt(nr, volume, stereo_position, type);
12078 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12080 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12081 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12082 y < LEVELY(BY1) ? LEVELY(BY1) :
12083 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12087 static void PlayLevelSoundAction(int x, int y, int action)
12089 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12092 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12094 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12096 if (sound_effect != SND_UNDEFINED)
12097 PlayLevelSound(x, y, sound_effect);
12100 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12103 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12105 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12106 PlayLevelSound(x, y, sound_effect);
12109 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12111 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12113 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12114 PlayLevelSound(x, y, sound_effect);
12117 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12119 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12121 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12122 StopSound(sound_effect);
12125 static void PlayLevelMusic()
12127 if (levelset.music[level_nr] != MUS_UNDEFINED)
12128 PlayMusic(levelset.music[level_nr]); /* from config file */
12130 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12133 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12135 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12136 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12137 int x = xx - 1 - offset;
12138 int y = yy - 1 - offset;
12143 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12147 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12151 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12155 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12159 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12163 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12167 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12170 case SAMPLE_android_clone:
12171 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12174 case SAMPLE_android_move:
12175 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12178 case SAMPLE_spring:
12179 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12183 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12187 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12190 case SAMPLE_eater_eat:
12191 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12195 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12198 case SAMPLE_collect:
12199 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12202 case SAMPLE_diamond:
12203 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12206 case SAMPLE_squash:
12207 /* !!! CHECK THIS !!! */
12209 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12211 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12215 case SAMPLE_wonderfall:
12216 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12220 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12224 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12228 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12232 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12236 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12240 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12243 case SAMPLE_wonder:
12244 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12248 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12251 case SAMPLE_exit_open:
12252 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12255 case SAMPLE_exit_leave:
12256 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12259 case SAMPLE_dynamite:
12260 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12264 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12268 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12272 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12276 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12280 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12284 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12288 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12294 void ChangeTime(int value)
12296 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12300 /* EMC game engine uses value from time counter of RND game engine */
12301 level.native_em_level->lev->time = *time;
12303 DrawGameValue_Time(*time);
12306 void RaiseScore(int value)
12308 /* EMC game engine and RND game engine have separate score counters */
12309 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12310 &level.native_em_level->lev->score : &local_player->score);
12314 DrawGameValue_Score(*score);
12318 void RaiseScore(int value)
12320 local_player->score += value;
12322 DrawGameValue_Score(local_player->score);
12325 void RaiseScoreElement(int element)
12330 case EL_BD_DIAMOND:
12331 case EL_EMERALD_YELLOW:
12332 case EL_EMERALD_RED:
12333 case EL_EMERALD_PURPLE:
12334 case EL_SP_INFOTRON:
12335 RaiseScore(level.score[SC_EMERALD]);
12338 RaiseScore(level.score[SC_DIAMOND]);
12341 RaiseScore(level.score[SC_CRYSTAL]);
12344 RaiseScore(level.score[SC_PEARL]);
12347 case EL_BD_BUTTERFLY:
12348 case EL_SP_ELECTRON:
12349 RaiseScore(level.score[SC_BUG]);
12352 case EL_BD_FIREFLY:
12353 case EL_SP_SNIKSNAK:
12354 RaiseScore(level.score[SC_SPACESHIP]);
12357 case EL_DARK_YAMYAM:
12358 RaiseScore(level.score[SC_YAMYAM]);
12361 RaiseScore(level.score[SC_ROBOT]);
12364 RaiseScore(level.score[SC_PACMAN]);
12367 RaiseScore(level.score[SC_NUT]);
12370 case EL_EM_DYNAMITE:
12371 case EL_SP_DISK_RED:
12372 case EL_DYNABOMB_INCREASE_NUMBER:
12373 case EL_DYNABOMB_INCREASE_SIZE:
12374 case EL_DYNABOMB_INCREASE_POWER:
12375 RaiseScore(level.score[SC_DYNAMITE]);
12377 case EL_SHIELD_NORMAL:
12378 case EL_SHIELD_DEADLY:
12379 RaiseScore(level.score[SC_SHIELD]);
12381 case EL_EXTRA_TIME:
12382 RaiseScore(level.extra_time_score);
12396 RaiseScore(level.score[SC_KEY]);
12399 RaiseScore(element_info[element].collect_score);
12404 void RequestQuitGame(boolean ask_if_really_quit)
12406 if (AllPlayersGone ||
12407 !ask_if_really_quit ||
12408 level_editor_test_game ||
12409 Request("Do you really want to quit the game ?",
12410 REQ_ASK | REQ_STAY_CLOSED))
12412 #if defined(NETWORK_AVALIABLE)
12413 if (options.network)
12414 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12418 if (!ask_if_really_quit || level_editor_test_game)
12420 game_status = GAME_MODE_MAIN;
12426 FadeOut(REDRAW_FIELD);
12428 game_status = GAME_MODE_MAIN;
12430 DrawAndFadeInMainMenu(REDRAW_FIELD);
12436 if (tape.playing && tape.deactivate_display)
12437 TapeDeactivateDisplayOff(TRUE);
12439 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12441 if (tape.playing && tape.deactivate_display)
12442 TapeDeactivateDisplayOn();
12447 /* ------------------------------------------------------------------------- */
12448 /* random generator functions */
12449 /* ------------------------------------------------------------------------- */
12451 unsigned int InitEngineRandom_RND(long seed)
12453 game.num_random_calls = 0;
12456 unsigned int rnd_seed = InitEngineRandom(seed);
12458 printf("::: START RND: %d\n", rnd_seed);
12463 return InitEngineRandom(seed);
12469 unsigned int RND(int max)
12473 game.num_random_calls++;
12475 return GetEngineRandom(max);
12482 /* ------------------------------------------------------------------------- */
12483 /* game engine snapshot handling functions */
12484 /* ------------------------------------------------------------------------- */
12486 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12488 struct EngineSnapshotInfo
12490 /* runtime values for custom element collect score */
12491 int collect_score[NUM_CUSTOM_ELEMENTS];
12493 /* runtime values for group element choice position */
12494 int choice_pos[NUM_GROUP_ELEMENTS];
12496 /* runtime values for belt position animations */
12497 int belt_graphic[4 * NUM_BELT_PARTS];
12498 int belt_anim_mode[4 * NUM_BELT_PARTS];
12501 struct EngineSnapshotNodeInfo
12508 static struct EngineSnapshotInfo engine_snapshot_rnd;
12509 static ListNode *engine_snapshot_list = NULL;
12510 static char *snapshot_level_identifier = NULL;
12511 static int snapshot_level_nr = -1;
12513 void FreeEngineSnapshot()
12515 while (engine_snapshot_list != NULL)
12516 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12519 setString(&snapshot_level_identifier, NULL);
12520 snapshot_level_nr = -1;
12523 static void SaveEngineSnapshotValues_RND()
12525 static int belt_base_active_element[4] =
12527 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12528 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12529 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12530 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12534 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12536 int element = EL_CUSTOM_START + i;
12538 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12541 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12543 int element = EL_GROUP_START + i;
12545 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12548 for (i = 0; i < 4; i++)
12550 for (j = 0; j < NUM_BELT_PARTS; j++)
12552 int element = belt_base_active_element[i] + j;
12553 int graphic = el2img(element);
12554 int anim_mode = graphic_info[graphic].anim_mode;
12556 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12557 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12562 static void LoadEngineSnapshotValues_RND()
12564 unsigned long num_random_calls = game.num_random_calls;
12567 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12569 int element = EL_CUSTOM_START + i;
12571 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12574 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12576 int element = EL_GROUP_START + i;
12578 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12581 for (i = 0; i < 4; i++)
12583 for (j = 0; j < NUM_BELT_PARTS; j++)
12585 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12586 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12588 graphic_info[graphic].anim_mode = anim_mode;
12592 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12594 InitRND(tape.random_seed);
12595 for (i = 0; i < num_random_calls; i++)
12599 if (game.num_random_calls != num_random_calls)
12601 Error(ERR_RETURN, "number of random calls out of sync");
12602 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12603 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12604 Error(ERR_EXIT, "this should not happen -- please debug");
12608 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12610 struct EngineSnapshotNodeInfo *bi =
12611 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12613 bi->buffer_orig = buffer;
12614 bi->buffer_copy = checked_malloc(size);
12617 memcpy(bi->buffer_copy, buffer, size);
12619 addNodeToList(&engine_snapshot_list, NULL, bi);
12622 void SaveEngineSnapshot()
12624 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12626 /* copy some special values to a structure better suited for the snapshot */
12628 SaveEngineSnapshotValues_RND();
12629 SaveEngineSnapshotValues_EM();
12631 /* save values stored in special snapshot structure */
12633 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12634 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12636 /* save further RND engine values */
12638 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12639 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12640 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12642 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12643 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12644 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12645 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12649 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12650 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12651 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12654 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12655 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12657 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12659 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12661 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12662 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12665 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12666 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12667 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12668 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12669 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12671 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12672 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12674 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12677 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12682 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12694 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12701 /* save level identification information */
12703 setString(&snapshot_level_identifier, leveldir_current->identifier);
12704 snapshot_level_nr = level_nr;
12707 ListNode *node = engine_snapshot_list;
12710 while (node != NULL)
12712 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12717 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12721 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12723 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12726 void LoadEngineSnapshot()
12728 ListNode *node = engine_snapshot_list;
12730 if (engine_snapshot_list == NULL)
12733 while (node != NULL)
12735 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12740 /* restore special values from snapshot structure */
12742 LoadEngineSnapshotValues_RND();
12743 LoadEngineSnapshotValues_EM();
12746 boolean CheckEngineSnapshot()
12748 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12749 snapshot_level_nr == level_nr);
12753 /* ---------- new game button stuff ---------------------------------------- */
12755 /* graphic position values for game buttons */
12756 #define GAME_BUTTON_XSIZE 30
12757 #define GAME_BUTTON_YSIZE 30
12758 #define GAME_BUTTON_XPOS 5
12759 #define GAME_BUTTON_YPOS 215
12760 #define SOUND_BUTTON_XPOS 5
12761 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12763 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12764 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12765 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12766 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12767 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12768 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12775 } gamebutton_info[NUM_GAME_BUTTONS] =
12778 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12783 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12784 GAME_CTRL_ID_PAUSE,
12788 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12793 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12794 SOUND_CTRL_ID_MUSIC,
12795 "background music on/off"
12798 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12799 SOUND_CTRL_ID_LOOPS,
12800 "sound loops on/off"
12803 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12804 SOUND_CTRL_ID_SIMPLE,
12805 "normal sounds on/off"
12809 void CreateGameButtons()
12813 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12815 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12816 struct GadgetInfo *gi;
12819 unsigned long event_mask;
12820 int gd_xoffset, gd_yoffset;
12821 int gd_x1, gd_x2, gd_y1, gd_y2;
12824 gd_xoffset = gamebutton_info[i].x;
12825 gd_yoffset = gamebutton_info[i].y;
12826 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12827 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12829 if (id == GAME_CTRL_ID_STOP ||
12830 id == GAME_CTRL_ID_PAUSE ||
12831 id == GAME_CTRL_ID_PLAY)
12833 button_type = GD_TYPE_NORMAL_BUTTON;
12835 event_mask = GD_EVENT_RELEASED;
12836 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12837 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12841 button_type = GD_TYPE_CHECK_BUTTON;
12843 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12844 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12845 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12846 event_mask = GD_EVENT_PRESSED;
12847 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12848 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12851 gi = CreateGadget(GDI_CUSTOM_ID, id,
12852 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12853 GDI_X, DX + gd_xoffset,
12854 GDI_Y, DY + gd_yoffset,
12855 GDI_WIDTH, GAME_BUTTON_XSIZE,
12856 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12857 GDI_TYPE, button_type,
12858 GDI_STATE, GD_BUTTON_UNPRESSED,
12859 GDI_CHECKED, checked,
12860 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12861 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12862 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12863 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12864 GDI_EVENT_MASK, event_mask,
12865 GDI_CALLBACK_ACTION, HandleGameButtons,
12869 Error(ERR_EXIT, "cannot create gadget");
12871 game_gadget[id] = gi;
12875 void FreeGameButtons()
12879 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12880 FreeGadget(game_gadget[i]);
12883 static void MapGameButtons()
12887 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12888 MapGadget(game_gadget[i]);
12891 void UnmapGameButtons()
12895 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12896 UnmapGadget(game_gadget[i]);
12899 static void HandleGameButtons(struct GadgetInfo *gi)
12901 int id = gi->custom_id;
12903 if (game_status != GAME_MODE_PLAYING)
12908 case GAME_CTRL_ID_STOP:
12912 RequestQuitGame(TRUE);
12915 case GAME_CTRL_ID_PAUSE:
12916 if (options.network)
12918 #if defined(NETWORK_AVALIABLE)
12920 SendToServer_ContinuePlaying();
12922 SendToServer_PausePlaying();
12926 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12929 case GAME_CTRL_ID_PLAY:
12932 #if defined(NETWORK_AVALIABLE)
12933 if (options.network)
12934 SendToServer_ContinuePlaying();
12938 tape.pausing = FALSE;
12939 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
12944 case SOUND_CTRL_ID_MUSIC:
12945 if (setup.sound_music)
12947 setup.sound_music = FALSE;
12950 else if (audio.music_available)
12952 setup.sound = setup.sound_music = TRUE;
12954 SetAudioMode(setup.sound);
12960 case SOUND_CTRL_ID_LOOPS:
12961 if (setup.sound_loops)
12962 setup.sound_loops = FALSE;
12963 else if (audio.loops_available)
12965 setup.sound = setup.sound_loops = TRUE;
12966 SetAudioMode(setup.sound);
12970 case SOUND_CTRL_ID_SIMPLE:
12971 if (setup.sound_simple)
12972 setup.sound_simple = FALSE;
12973 else if (audio.sound_available)
12975 setup.sound = setup.sound_simple = TRUE;
12976 SetAudioMode(setup.sound);