1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
55 #define USE_NEW_GAME_WON (USE_NEW_STUFF * 1)
63 /* for MovePlayer() */
64 #define MP_NO_ACTION 0
67 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
69 /* for ScrollPlayer() */
71 #define SCROLL_GO_ON 1
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START 0
75 #define EX_TYPE_NONE 0
76 #define EX_TYPE_NORMAL (1 << 0)
77 #define EX_TYPE_CENTER (1 << 1)
78 #define EX_TYPE_BORDER (1 << 2)
79 #define EX_TYPE_CROSS (1 << 3)
80 #define EX_TYPE_DYNA (1 << 4)
81 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
85 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
87 /* special positions in the game control window (relative to control window) */
88 #define XX_LEVEL1 (game.panel.level.x)
89 #define XX_LEVEL2 (game.panel.level.x - 1)
90 #define YY_LEVEL (game.panel.level.y)
91 #define XX_EMERALDS (game.panel.gems.x)
92 #define YY_EMERALDS (game.panel.gems.y)
93 #define XX_DYNAMITE (game.panel.inventory.x)
94 #define YY_DYNAMITE (game.panel.inventory.y)
95 #define XX_KEYS (game.panel.keys.x)
96 #define YY_KEYS (game.panel.keys.y)
97 #define XX_SCORE (game.panel.score.x)
98 #define YY_SCORE (game.panel.score.y)
99 #define XX_TIME1 (game.panel.time.x)
100 #define XX_TIME2 (game.panel.time.x + 1)
101 #define YY_TIME (game.panel.time.y)
105 /* special positions in the game control window (relative to control window) */
108 #define XX_EMERALDS 29
109 #define YY_EMERALDS 54
110 #define XX_DYNAMITE 29
111 #define YY_DYNAMITE 89
122 /* special positions in the game control window (relative to main window) */
123 #define DX_LEVEL1 (DX + XX_LEVEL1)
124 #define DX_LEVEL2 (DX + XX_LEVEL2)
125 #define DY_LEVEL (DY + YY_LEVEL)
126 #define DX_EMERALDS (DX + XX_EMERALDS)
127 #define DY_EMERALDS (DY + YY_EMERALDS)
128 #define DX_DYNAMITE (DX + XX_DYNAMITE)
129 #define DY_DYNAMITE (DY + YY_DYNAMITE)
130 #define DX_KEYS (DX + XX_KEYS)
131 #define DY_KEYS (DY + YY_KEYS)
132 #define DX_SCORE (DX + XX_SCORE)
133 #define DY_SCORE (DY + YY_SCORE)
134 #define DX_TIME1 (DX + XX_TIME1)
135 #define DX_TIME2 (DX + XX_TIME2)
136 #define DY_TIME (DY + YY_TIME)
138 /* values for delayed check of falling and moving elements and for collision */
139 #define CHECK_DELAY_MOVING 3
140 #define CHECK_DELAY_FALLING 3
141 #define CHECK_DELAY_COLLISION 2
143 /* values for initial player move delay (initial delay counter value) */
144 #define INITIAL_MOVE_DELAY_OFF -1
145 #define INITIAL_MOVE_DELAY_ON 0
147 /* values for player movement speed (which is in fact a delay value) */
148 #define MOVE_DELAY_MIN_SPEED 32
149 #define MOVE_DELAY_NORMAL_SPEED 8
150 #define MOVE_DELAY_HIGH_SPEED 4
151 #define MOVE_DELAY_MAX_SPEED 1
154 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
155 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
157 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
158 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
160 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
161 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
163 /* values for other actions */
164 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
165 #define MOVE_STEPSIZE_MIN (1)
166 #define MOVE_STEPSIZE_MAX (TILEX)
168 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
169 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
171 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
173 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
174 RND(element_info[e].push_delay_random))
175 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
176 RND(element_info[e].drop_delay_random))
177 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
178 RND(element_info[e].move_delay_random))
179 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
180 (element_info[e].move_delay_random))
181 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
182 RND(element_info[e].ce_value_random_initial))
183 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
184 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
185 RND((c)->delay_random * (c)->delay_frames))
186 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
187 RND((c)->delay_random))
191 #define GET_VALID_RUNTIME_ELEMENT(e) \
192 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
194 #define GET_VALID_FILE_ELEMENT(e) \
195 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
198 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
199 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
200 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
201 (be) + (e) - EL_SELF)
203 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
204 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
205 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
206 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
207 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
208 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
209 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
210 (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ? \
211 RESOLVED_REFERENCE_ELEMENT(be, e) : \
214 #define CAN_GROW_INTO(e) \
215 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
217 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
218 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
221 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
222 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
223 (CAN_MOVE_INTO_ACID(e) && \
224 Feld[x][y] == EL_ACID) || \
227 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
228 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
229 (CAN_MOVE_INTO_ACID(e) && \
230 Feld[x][y] == EL_ACID) || \
233 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
234 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
236 (CAN_MOVE_INTO_ACID(e) && \
237 Feld[x][y] == EL_ACID) || \
238 (DONT_COLLIDE_WITH(e) && \
240 !PLAYER_ENEMY_PROTECTED(x, y))))
242 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
243 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
245 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
246 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
248 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
249 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
251 #define ANDROID_CAN_CLONE_FIELD(x, y) \
252 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
253 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
255 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
258 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
261 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
262 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
264 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
265 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
267 #define PIG_CAN_ENTER_FIELD(e, x, y) \
268 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
270 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
271 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
272 IS_FOOD_PENGUIN(Feld[x][y])))
273 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
274 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
276 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
277 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
279 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
280 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
282 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
283 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
284 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
287 #define GROUP_NR(e) ((e) - EL_GROUP_START)
288 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
289 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
291 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
292 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
295 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
297 #define CE_ENTER_FIELD_COND(e, x, y) \
298 (!IS_PLAYER(x, y) && \
299 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
301 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
302 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
304 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
305 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
307 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
308 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
309 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
310 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
312 /* game button identifiers */
313 #define GAME_CTRL_ID_STOP 0
314 #define GAME_CTRL_ID_PAUSE 1
315 #define GAME_CTRL_ID_PLAY 2
316 #define SOUND_CTRL_ID_MUSIC 3
317 #define SOUND_CTRL_ID_LOOPS 4
318 #define SOUND_CTRL_ID_SIMPLE 5
320 #define NUM_GAME_BUTTONS 6
323 /* forward declaration for internal use */
325 static void CreateField(int, int, int);
327 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
328 static void AdvanceFrameAndPlayerCounters(int);
330 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
331 static boolean MovePlayer(struct PlayerInfo *, int, int);
332 static void ScrollPlayer(struct PlayerInfo *, int);
333 static void ScrollScreen(struct PlayerInfo *, int);
335 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
337 static void InitBeltMovement(void);
338 static void CloseAllOpenTimegates(void);
339 static void CheckGravityMovement(struct PlayerInfo *);
340 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
341 static void KillPlayerUnlessEnemyProtected(int, int);
342 static void KillPlayerUnlessExplosionProtected(int, int);
344 static void TestIfPlayerTouchesCustomElement(int, int);
345 static void TestIfElementTouchesCustomElement(int, int);
346 static void TestIfElementHitsCustomElement(int, int, int);
348 static void TestIfElementSmashesCustomElement(int, int, int);
351 static void HandleElementChange(int, int, int);
352 static void ExecuteCustomElementAction(int, int, int, int);
353 static boolean ChangeElement(int, int, int, int);
355 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
356 #define CheckTriggeredElementChange(x, y, e, ev) \
357 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
358 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
359 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
360 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
361 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
362 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
363 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
365 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
366 #define CheckElementChange(x, y, e, te, ev) \
367 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
368 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
369 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
370 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
371 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
373 static void PlayLevelSound(int, int, int);
374 static void PlayLevelSoundNearest(int, int, int);
375 static void PlayLevelSoundAction(int, int, int);
376 static void PlayLevelSoundElementAction(int, int, int, int);
377 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
378 static void PlayLevelSoundActionIfLoop(int, int, int);
379 static void StopLevelSoundActionIfLoop(int, int, int);
380 static void PlayLevelMusic();
382 static void MapGameButtons();
383 static void HandleGameButtons(struct GadgetInfo *);
385 int AmoebeNachbarNr(int, int);
386 void AmoebeUmwandeln(int, int);
387 void ContinueMoving(int, int);
389 void InitMovDir(int, int);
390 void InitAmoebaNr(int, int);
391 int NewHiScore(void);
393 void TestIfGoodThingHitsBadThing(int, int, int);
394 void TestIfBadThingHitsGoodThing(int, int, int);
395 void TestIfPlayerTouchesBadThing(int, int);
396 void TestIfPlayerRunsIntoBadThing(int, int, int);
397 void TestIfBadThingTouchesPlayer(int, int);
398 void TestIfBadThingRunsIntoPlayer(int, int, int);
399 void TestIfFriendTouchesBadThing(int, int);
400 void TestIfBadThingTouchesFriend(int, int);
401 void TestIfBadThingTouchesOtherBadThing(int, int);
403 void KillPlayer(struct PlayerInfo *);
404 void BuryPlayer(struct PlayerInfo *);
405 void RemovePlayer(struct PlayerInfo *);
407 boolean SnapField(struct PlayerInfo *, int, int);
408 boolean DropElement(struct PlayerInfo *);
410 static int getInvisibleActiveFromInvisibleElement(int);
411 static int getInvisibleFromInvisibleActiveElement(int);
413 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
416 /* ------------------------------------------------------------------------- */
417 /* definition of elements that automatically change to other elements after */
418 /* a specified time, eventually calling a function when changing */
419 /* ------------------------------------------------------------------------- */
421 /* forward declaration for changer functions */
422 static void InitBuggyBase(int, int);
423 static void WarnBuggyBase(int, int);
425 static void InitTrap(int, int);
426 static void ActivateTrap(int, int);
427 static void ChangeActiveTrap(int, int);
429 static void InitRobotWheel(int, int);
430 static void RunRobotWheel(int, int);
431 static void StopRobotWheel(int, int);
433 static void InitTimegateWheel(int, int);
434 static void RunTimegateWheel(int, int);
436 static void InitMagicBallDelay(int, int);
437 static void ActivateMagicBall(int, int);
439 static void InitDiagonalMovingElement(int, int);
441 struct ChangingElementInfo
446 void (*pre_change_function)(int x, int y);
447 void (*change_function)(int x, int y);
448 void (*post_change_function)(int x, int y);
451 static struct ChangingElementInfo change_delay_list[] =
502 EL_SWITCHGATE_OPENING,
510 EL_SWITCHGATE_CLOSING,
511 EL_SWITCHGATE_CLOSED,
543 EL_ACID_SPLASH_RIGHT,
552 EL_SP_BUGGY_BASE_ACTIVATING,
559 EL_SP_BUGGY_BASE_ACTIVATING,
560 EL_SP_BUGGY_BASE_ACTIVE,
567 EL_SP_BUGGY_BASE_ACTIVE,
591 EL_ROBOT_WHEEL_ACTIVE,
599 EL_TIMEGATE_SWITCH_ACTIVE,
607 EL_EMC_MAGIC_BALL_ACTIVE,
608 EL_EMC_MAGIC_BALL_ACTIVE,
615 EL_EMC_SPRING_BUMPER_ACTIVE,
616 EL_EMC_SPRING_BUMPER,
623 EL_DIAGONAL_SHRINKING,
636 InitDiagonalMovingElement
652 int push_delay_fixed, push_delay_random;
657 { EL_BALLOON, 0, 0 },
659 { EL_SOKOBAN_OBJECT, 2, 0 },
660 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
661 { EL_SATELLITE, 2, 0 },
662 { EL_SP_DISK_YELLOW, 2, 0 },
664 { EL_UNDEFINED, 0, 0 },
672 move_stepsize_list[] =
674 { EL_AMOEBA_DROP, 2 },
675 { EL_AMOEBA_DROPPING, 2 },
676 { EL_QUICKSAND_FILLING, 1 },
677 { EL_QUICKSAND_EMPTYING, 1 },
678 { EL_MAGIC_WALL_FILLING, 2 },
679 { EL_BD_MAGIC_WALL_FILLING, 2 },
680 { EL_MAGIC_WALL_EMPTYING, 2 },
681 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
691 collect_count_list[] =
694 { EL_BD_DIAMOND, 1 },
695 { EL_EMERALD_YELLOW, 1 },
696 { EL_EMERALD_RED, 1 },
697 { EL_EMERALD_PURPLE, 1 },
699 { EL_SP_INFOTRON, 1 },
711 access_direction_list[] =
713 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
714 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
715 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
716 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
717 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
718 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
719 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
720 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
721 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
722 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
723 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
725 { EL_SP_PORT_LEFT, MV_RIGHT },
726 { EL_SP_PORT_RIGHT, MV_LEFT },
727 { EL_SP_PORT_UP, MV_DOWN },
728 { EL_SP_PORT_DOWN, MV_UP },
729 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
730 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
731 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
732 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
733 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
734 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
735 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
736 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
737 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
738 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
739 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
740 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
741 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
742 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
743 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
745 { EL_UNDEFINED, MV_NONE }
748 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
750 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
751 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
752 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
753 IS_JUST_CHANGING(x, y))
755 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
757 /* static variables for playfield scan mode (scanning forward or backward) */
758 static int playfield_scan_start_x = 0;
759 static int playfield_scan_start_y = 0;
760 static int playfield_scan_delta_x = 1;
761 static int playfield_scan_delta_y = 1;
763 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
764 (y) >= 0 && (y) <= lev_fieldy - 1; \
765 (y) += playfield_scan_delta_y) \
766 for ((x) = playfield_scan_start_x; \
767 (x) >= 0 && (x) <= lev_fieldx - 1; \
768 (x) += playfield_scan_delta_x) \
771 void DEBUG_SetMaximumDynamite()
775 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
776 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
777 local_player->inventory_element[local_player->inventory_size++] =
782 static void InitPlayfieldScanModeVars()
784 if (game.use_reverse_scan_direction)
786 playfield_scan_start_x = lev_fieldx - 1;
787 playfield_scan_start_y = lev_fieldy - 1;
789 playfield_scan_delta_x = -1;
790 playfield_scan_delta_y = -1;
794 playfield_scan_start_x = 0;
795 playfield_scan_start_y = 0;
797 playfield_scan_delta_x = 1;
798 playfield_scan_delta_y = 1;
802 static void InitPlayfieldScanMode(int mode)
804 game.use_reverse_scan_direction =
805 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
807 InitPlayfieldScanModeVars();
810 static int get_move_delay_from_stepsize(int move_stepsize)
813 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
815 /* make sure that stepsize value is always a power of 2 */
816 move_stepsize = (1 << log_2(move_stepsize));
818 return TILEX / move_stepsize;
821 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
824 int player_nr = player->index_nr;
825 int move_delay = get_move_delay_from_stepsize(move_stepsize);
826 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
828 /* do no immediately change move delay -- the player might just be moving */
829 player->move_delay_value_next = move_delay;
831 /* information if player can move must be set separately */
832 player->cannot_move = cannot_move;
836 player->move_delay = game.initial_move_delay[player_nr];
837 player->move_delay_value = game.initial_move_delay_value[player_nr];
839 player->move_delay_value_next = -1;
841 player->move_delay_reset_counter = 0;
845 void GetPlayerConfig()
847 if (!audio.sound_available)
848 setup.sound_simple = FALSE;
850 if (!audio.loops_available)
851 setup.sound_loops = FALSE;
853 if (!audio.music_available)
854 setup.sound_music = FALSE;
856 if (!video.fullscreen_available)
857 setup.fullscreen = FALSE;
859 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
861 SetAudioMode(setup.sound);
865 static int getBeltNrFromBeltElement(int element)
867 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
868 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
869 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
872 static int getBeltNrFromBeltActiveElement(int element)
874 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
875 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
876 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
879 static int getBeltNrFromBeltSwitchElement(int element)
881 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
882 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
883 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
886 static int getBeltDirNrFromBeltSwitchElement(int element)
888 static int belt_base_element[4] =
890 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
891 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
892 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
893 EL_CONVEYOR_BELT_4_SWITCH_LEFT
896 int belt_nr = getBeltNrFromBeltSwitchElement(element);
897 int belt_dir_nr = element - belt_base_element[belt_nr];
899 return (belt_dir_nr % 3);
902 static int getBeltDirFromBeltSwitchElement(int element)
904 static int belt_move_dir[3] =
911 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
913 return belt_move_dir[belt_dir_nr];
916 static int get_element_from_group_element(int element)
918 if (IS_GROUP_ELEMENT(element))
920 struct ElementGroupInfo *group = element_info[element].group;
921 int last_anim_random_frame = gfx.anim_random_frame;
924 if (group->choice_mode == ANIM_RANDOM)
925 gfx.anim_random_frame = RND(group->num_elements_resolved);
927 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
928 group->choice_mode, 0,
931 if (group->choice_mode == ANIM_RANDOM)
932 gfx.anim_random_frame = last_anim_random_frame;
936 element = group->element_resolved[element_pos];
942 static void InitPlayerField(int x, int y, int element, boolean init_game)
944 if (element == EL_SP_MURPHY)
948 if (stored_player[0].present)
950 Feld[x][y] = EL_SP_MURPHY_CLONE;
956 stored_player[0].use_murphy = TRUE;
958 if (!level.use_artwork_element[0])
959 stored_player[0].artwork_element = EL_SP_MURPHY;
962 Feld[x][y] = EL_PLAYER_1;
968 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
969 int jx = player->jx, jy = player->jy;
971 player->present = TRUE;
973 player->block_last_field = (element == EL_SP_MURPHY ?
974 level.sp_block_last_field :
975 level.block_last_field);
977 /* ---------- initialize player's last field block delay --------------- */
979 /* always start with reliable default value (no adjustment needed) */
980 player->block_delay_adjustment = 0;
982 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
983 if (player->block_last_field && element == EL_SP_MURPHY)
984 player->block_delay_adjustment = 1;
986 /* special case 2: in game engines before 3.1.1, blocking was different */
987 if (game.use_block_last_field_bug)
988 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
990 if (!options.network || player->connected)
992 player->active = TRUE;
994 /* remove potentially duplicate players */
995 if (StorePlayer[jx][jy] == Feld[x][y])
996 StorePlayer[jx][jy] = 0;
998 StorePlayer[x][y] = Feld[x][y];
1002 printf("Player %d activated.\n", player->element_nr);
1003 printf("[Local player is %d and currently %s.]\n",
1004 local_player->element_nr,
1005 local_player->active ? "active" : "not active");
1009 Feld[x][y] = EL_EMPTY;
1011 player->jx = player->last_jx = x;
1012 player->jy = player->last_jy = y;
1016 static void InitField(int x, int y, boolean init_game)
1018 int element = Feld[x][y];
1027 InitPlayerField(x, y, element, init_game);
1030 case EL_SOKOBAN_FIELD_PLAYER:
1031 element = Feld[x][y] = EL_PLAYER_1;
1032 InitField(x, y, init_game);
1034 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1035 InitField(x, y, init_game);
1038 case EL_SOKOBAN_FIELD_EMPTY:
1039 local_player->sokobanfields_still_needed++;
1043 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1044 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1045 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1046 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1047 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1048 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1049 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1050 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1051 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1052 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1061 case EL_SPACESHIP_RIGHT:
1062 case EL_SPACESHIP_UP:
1063 case EL_SPACESHIP_LEFT:
1064 case EL_SPACESHIP_DOWN:
1065 case EL_BD_BUTTERFLY:
1066 case EL_BD_BUTTERFLY_RIGHT:
1067 case EL_BD_BUTTERFLY_UP:
1068 case EL_BD_BUTTERFLY_LEFT:
1069 case EL_BD_BUTTERFLY_DOWN:
1071 case EL_BD_FIREFLY_RIGHT:
1072 case EL_BD_FIREFLY_UP:
1073 case EL_BD_FIREFLY_LEFT:
1074 case EL_BD_FIREFLY_DOWN:
1075 case EL_PACMAN_RIGHT:
1077 case EL_PACMAN_LEFT:
1078 case EL_PACMAN_DOWN:
1080 case EL_YAMYAM_LEFT:
1081 case EL_YAMYAM_RIGHT:
1083 case EL_YAMYAM_DOWN:
1084 case EL_DARK_YAMYAM:
1087 case EL_SP_SNIKSNAK:
1088 case EL_SP_ELECTRON:
1097 case EL_AMOEBA_FULL:
1102 case EL_AMOEBA_DROP:
1103 if (y == lev_fieldy - 1)
1105 Feld[x][y] = EL_AMOEBA_GROWING;
1106 Store[x][y] = EL_AMOEBA_WET;
1110 case EL_DYNAMITE_ACTIVE:
1111 case EL_SP_DISK_RED_ACTIVE:
1112 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1113 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1114 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1115 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1116 MovDelay[x][y] = 96;
1119 case EL_EM_DYNAMITE_ACTIVE:
1120 MovDelay[x][y] = 32;
1124 local_player->lights_still_needed++;
1128 local_player->friends_still_needed++;
1133 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1136 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1137 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1138 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1139 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1140 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1141 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1142 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1143 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1144 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1145 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1146 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1147 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1150 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1151 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1152 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1154 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1156 game.belt_dir[belt_nr] = belt_dir;
1157 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1159 else /* more than one switch -- set it like the first switch */
1161 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1166 #if !USE_BOTH_SWITCHGATE_SWITCHES
1167 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1169 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1173 case EL_LIGHT_SWITCH_ACTIVE:
1175 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1178 case EL_INVISIBLE_STEELWALL:
1179 case EL_INVISIBLE_WALL:
1180 case EL_INVISIBLE_SAND:
1181 if (game.light_time_left > 0 ||
1182 game.lenses_time_left > 0)
1183 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1186 case EL_EMC_MAGIC_BALL:
1187 if (game.ball_state)
1188 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1191 case EL_EMC_MAGIC_BALL_SWITCH:
1192 if (game.ball_state)
1193 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1198 if (IS_CUSTOM_ELEMENT(element))
1200 if (CAN_MOVE(element))
1203 #if USE_NEW_CUSTOM_VALUE
1204 if (!element_info[element].use_last_ce_value || init_game)
1205 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1209 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1212 else if (IS_GROUP_ELEMENT(element))
1215 Feld[x][y] = get_element_from_group_element(element);
1217 InitField(x, y, init_game);
1219 struct ElementGroupInfo *group = element_info[element].group;
1220 int last_anim_random_frame = gfx.anim_random_frame;
1223 if (group->choice_mode == ANIM_RANDOM)
1224 gfx.anim_random_frame = RND(group->num_elements_resolved);
1226 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1227 group->choice_mode, 0,
1230 if (group->choice_mode == ANIM_RANDOM)
1231 gfx.anim_random_frame = last_anim_random_frame;
1233 group->choice_pos++;
1235 Feld[x][y] = group->element_resolved[element_pos];
1237 InitField(x, y, init_game);
1246 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1251 #if USE_NEW_CUSTOM_VALUE
1254 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1256 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1264 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1266 InitField(x, y, init_game);
1268 /* not needed to call InitMovDir() -- already done by InitField()! */
1269 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1270 CAN_MOVE(Feld[x][y]))
1274 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1276 int old_element = Feld[x][y];
1278 InitField(x, y, init_game);
1280 /* not needed to call InitMovDir() -- already done by InitField()! */
1281 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1282 CAN_MOVE(old_element) &&
1283 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1286 /* this case is in fact a combination of not less than three bugs:
1287 first, it calls InitMovDir() for elements that can move, although this is
1288 already done by InitField(); then, it checks the element that was at this
1289 field _before_ the call to InitField() (which can change it); lastly, it
1290 was not called for "mole with direction" elements, which were treated as
1291 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1295 inline void DrawGameValue_Emeralds(int value)
1297 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1299 if (PANEL_DEACTIVATED(game.panel.gems))
1302 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1305 inline void DrawGameValue_Dynamite(int value)
1307 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1309 if (PANEL_DEACTIVATED(game.panel.inventory))
1312 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1315 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1317 int base_key_graphic = EL_KEY_1;
1320 if (PANEL_DEACTIVATED(game.panel.keys))
1323 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1324 base_key_graphic = EL_EM_KEY_1;
1326 /* currently only 4 of 8 possible keys are displayed */
1327 for (i = 0; i < STD_NUM_KEYS; i++)
1329 int x = XX_KEYS + i * MINI_TILEX;
1333 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1335 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1336 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1340 inline void DrawGameValue_Score(int value)
1342 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1344 if (PANEL_DEACTIVATED(game.panel.score))
1347 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1350 inline void DrawGameValue_Time(int value)
1352 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1353 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1355 if (PANEL_DEACTIVATED(game.panel.time))
1358 /* clear background if value just changed its size */
1359 if (value == 999 || value == 1000)
1360 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1363 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1365 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1368 inline void DrawGameValue_Level(int value)
1370 if (PANEL_DEACTIVATED(game.panel.level))
1374 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1377 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1380 /* misuse area for displaying emeralds to draw bigger level number */
1381 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1382 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1384 /* now copy it to the area for displaying level number */
1385 BlitBitmap(drawto, drawto,
1386 DX_EMERALDS, DY_EMERALDS + 1,
1387 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1388 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1389 DX_LEVEL - 1, DY_LEVEL + 1);
1391 /* restore the area for displaying emeralds */
1392 DrawGameValue_Emeralds(local_player->gems_still_needed);
1394 /* yes, this is all really ugly :-) */
1399 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1402 int key[MAX_NUM_KEYS];
1405 for (i = 0; i < MAX_NUM_KEYS; i++)
1406 key[i] = key_bits & (1 << i);
1408 DrawGameValue_Level(level_nr);
1410 DrawGameValue_Emeralds(emeralds);
1411 DrawGameValue_Dynamite(dynamite);
1412 DrawGameValue_Score(score);
1413 DrawGameValue_Time(time);
1415 DrawGameValue_Keys(key);
1418 void DrawGameDoorValues()
1420 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1421 int dynamite_state = 0;
1425 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1427 DrawGameDoorValues_EM();
1433 DrawGameValue_Level(level_nr);
1435 DrawGameValue_Emeralds(local_player->gems_still_needed);
1436 DrawGameValue_Dynamite(local_player->inventory_size);
1437 DrawGameValue_Score(local_player->score);
1438 DrawGameValue_Time(TimeLeft);
1442 if (game.centered_player_nr == -1)
1444 for (i = 0; i < MAX_PLAYERS; i++)
1446 for (j = 0; j < MAX_NUM_KEYS; j++)
1447 if (stored_player[i].key[j])
1448 key_bits |= (1 << j);
1450 dynamite_state += stored_player[i].inventory_size;
1454 DrawGameValue_Keys(stored_player[i].key);
1459 int player_nr = game.centered_player_nr;
1461 for (i = 0; i < MAX_NUM_KEYS; i++)
1462 if (stored_player[player_nr].key[i])
1463 key_bits |= (1 << i);
1465 dynamite_state = stored_player[player_nr].inventory_size;
1468 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1469 local_player->score, time_value, key_bits);
1474 static void resolve_group_element(int group_element, int recursion_depth)
1476 static int group_nr;
1477 static struct ElementGroupInfo *group;
1478 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1481 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1483 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1484 group_element - EL_GROUP_START + 1);
1486 /* replace element which caused too deep recursion by question mark */
1487 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1492 if (recursion_depth == 0) /* initialization */
1494 group = element_info[group_element].group;
1495 group_nr = group_element - EL_GROUP_START;
1497 group->num_elements_resolved = 0;
1498 group->choice_pos = 0;
1501 for (i = 0; i < actual_group->num_elements; i++)
1503 int element = actual_group->element[i];
1505 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1508 if (IS_GROUP_ELEMENT(element))
1509 resolve_group_element(element, recursion_depth + 1);
1512 group->element_resolved[group->num_elements_resolved++] = element;
1513 element_info[element].in_group[group_nr] = TRUE;
1520 static void replace_reference_element(int base_element, int *element)
1522 if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
1524 *element = base_element + *element - EL_SELF;
1525 *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
1526 *element > EL_CUSTOM_END ? EL_CUSTOM_END : *element);
1532 =============================================================================
1534 -----------------------------------------------------------------------------
1535 initialize game engine due to level / tape version number
1536 =============================================================================
1539 static void InitGameEngine()
1541 int i, j, k, l, x, y;
1543 /* set game engine from tape file when re-playing, else from level file */
1544 game.engine_version = (tape.playing ? tape.engine_version :
1545 level.game_version);
1547 /* ---------------------------------------------------------------------- */
1548 /* set flags for bugs and changes according to active game engine version */
1549 /* ---------------------------------------------------------------------- */
1552 Summary of bugfix/change:
1553 Fixed handling for custom elements that change when pushed by the player.
1555 Fixed/changed in version:
1559 Before 3.1.0, custom elements that "change when pushing" changed directly
1560 after the player started pushing them (until then handled in "DigField()").
1561 Since 3.1.0, these custom elements are not changed until the "pushing"
1562 move of the element is finished (now handled in "ContinueMoving()").
1564 Affected levels/tapes:
1565 The first condition is generally needed for all levels/tapes before version
1566 3.1.0, which might use the old behaviour before it was changed; known tapes
1567 that are affected are some tapes from the level set "Walpurgis Gardens" by
1569 The second condition is an exception from the above case and is needed for
1570 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1571 above (including some development versions of 3.1.0), but before it was
1572 known that this change would break tapes like the above and was fixed in
1573 3.1.1, so that the changed behaviour was active although the engine version
1574 while recording maybe was before 3.1.0. There is at least one tape that is
1575 affected by this exception, which is the tape for the one-level set "Bug
1576 Machine" by Juergen Bonhagen.
1579 game.use_change_when_pushing_bug =
1580 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1582 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1583 tape.game_version < VERSION_IDENT(3,1,1,0)));
1586 Summary of bugfix/change:
1587 Fixed handling for blocking the field the player leaves when moving.
1589 Fixed/changed in version:
1593 Before 3.1.1, when "block last field when moving" was enabled, the field
1594 the player is leaving when moving was blocked for the time of the move,
1595 and was directly unblocked afterwards. This resulted in the last field
1596 being blocked for exactly one less than the number of frames of one player
1597 move. Additionally, even when blocking was disabled, the last field was
1598 blocked for exactly one frame.
1599 Since 3.1.1, due to changes in player movement handling, the last field
1600 is not blocked at all when blocking is disabled. When blocking is enabled,
1601 the last field is blocked for exactly the number of frames of one player
1602 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1603 last field is blocked for exactly one more than the number of frames of
1606 Affected levels/tapes:
1607 (!!! yet to be determined -- probably many !!!)
1610 game.use_block_last_field_bug =
1611 (game.engine_version < VERSION_IDENT(3,1,1,0));
1614 Summary of bugfix/change:
1615 Changed behaviour of CE changes with multiple changes per single frame.
1617 Fixed/changed in version:
1621 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1622 This resulted in race conditions where CEs seem to behave strange in some
1623 situations (where triggered CE changes were just skipped because there was
1624 already a CE change on that tile in the playfield in that engine frame).
1625 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1626 (The number of changes per frame must be limited in any case, because else
1627 it is easily possible to define CE changes that would result in an infinite
1628 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1629 should be set large enough so that it would only be reached in cases where
1630 the corresponding CE change conditions run into a loop. Therefore, it seems
1631 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1632 maximal number of change pages for custom elements.)
1634 Affected levels/tapes:
1638 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1639 game.max_num_changes_per_frame = 1;
1641 game.max_num_changes_per_frame =
1642 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1645 /* ---------------------------------------------------------------------- */
1647 /* default scan direction: scan playfield from top/left to bottom/right */
1648 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1650 /* dynamically adjust element properties according to game engine version */
1651 InitElementPropertiesEngine(game.engine_version);
1654 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1655 printf(" tape version == %06d [%s] [file: %06d]\n",
1656 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1658 printf(" => game.engine_version == %06d\n", game.engine_version);
1662 /* ---------- recursively resolve group elements ------------------------- */
1664 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1665 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1666 element_info[i].in_group[j] = FALSE;
1668 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1669 resolve_group_element(EL_GROUP_START + i, 0);
1672 /* ---------- initialize player's initial move delay --------------------- */
1675 /* dynamically adjust player properties according to level information */
1676 for (i = 0; i < MAX_PLAYERS; i++)
1677 game.initial_move_delay_value[i] =
1678 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1680 /* dynamically adjust player properties according to level information */
1681 game.initial_move_delay_value =
1682 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1685 /* dynamically adjust player properties according to game engine version */
1686 for (i = 0; i < MAX_PLAYERS; i++)
1687 game.initial_move_delay[i] =
1688 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1689 game.initial_move_delay_value[i] : 0);
1691 /* ---------- initialize player's initial push delay --------------------- */
1693 /* dynamically adjust player properties according to game engine version */
1694 game.initial_push_delay_value =
1695 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1697 /* ---------- initialize changing elements ------------------------------- */
1699 /* initialize changing elements information */
1700 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1702 struct ElementInfo *ei = &element_info[i];
1704 /* this pointer might have been changed in the level editor */
1705 ei->change = &ei->change_page[0];
1707 if (!IS_CUSTOM_ELEMENT(i))
1709 ei->change->target_element = EL_EMPTY_SPACE;
1710 ei->change->delay_fixed = 0;
1711 ei->change->delay_random = 0;
1712 ei->change->delay_frames = 1;
1715 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1717 ei->has_change_event[j] = FALSE;
1719 ei->event_page_nr[j] = 0;
1720 ei->event_page[j] = &ei->change_page[0];
1724 /* add changing elements from pre-defined list */
1725 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1727 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1728 struct ElementInfo *ei = &element_info[ch_delay->element];
1730 ei->change->target_element = ch_delay->target_element;
1731 ei->change->delay_fixed = ch_delay->change_delay;
1733 ei->change->pre_change_function = ch_delay->pre_change_function;
1734 ei->change->change_function = ch_delay->change_function;
1735 ei->change->post_change_function = ch_delay->post_change_function;
1737 ei->change->can_change = TRUE;
1738 ei->change->can_change_or_has_action = TRUE;
1740 ei->has_change_event[CE_DELAY] = TRUE;
1742 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1743 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1746 /* ---------- initialize internal run-time variables ------------- */
1748 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1750 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1752 for (j = 0; j < ei->num_change_pages; j++)
1754 ei->change_page[j].can_change_or_has_action =
1755 (ei->change_page[j].can_change |
1756 ei->change_page[j].has_action);
1760 /* add change events from custom element configuration */
1761 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1763 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1765 for (j = 0; j < ei->num_change_pages; j++)
1767 if (!ei->change_page[j].can_change_or_has_action)
1770 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1772 /* only add event page for the first page found with this event */
1773 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1775 ei->has_change_event[k] = TRUE;
1777 ei->event_page_nr[k] = j;
1778 ei->event_page[k] = &ei->change_page[j];
1784 /* ---------- initialize run-time trigger player and element ------------- */
1786 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1788 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1790 for (j = 0; j < ei->num_change_pages; j++)
1792 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1793 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1794 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1795 ei->change_page[j].actual_trigger_ce_value = 0;
1796 ei->change_page[j].actual_trigger_ce_score = 0;
1800 /* ---------- initialize trigger events ---------------------------------- */
1802 /* initialize trigger events information */
1803 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1804 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1805 trigger_events[i][j] = FALSE;
1807 /* add trigger events from element change event properties */
1808 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1810 struct ElementInfo *ei = &element_info[i];
1812 for (j = 0; j < ei->num_change_pages; j++)
1814 if (!ei->change_page[j].can_change_or_has_action)
1817 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1819 int trigger_element = ei->change_page[j].trigger_element;
1821 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1823 if (ei->change_page[j].has_event[k])
1825 if (IS_GROUP_ELEMENT(trigger_element))
1827 struct ElementGroupInfo *group =
1828 element_info[trigger_element].group;
1830 for (l = 0; l < group->num_elements_resolved; l++)
1831 trigger_events[group->element_resolved[l]][k] = TRUE;
1834 else if (trigger_element == EL_ANY_ELEMENT)
1835 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1836 trigger_events[l][k] = TRUE;
1839 trigger_events[trigger_element][k] = TRUE;
1846 /* ---------- initialize push delay -------------------------------------- */
1848 /* initialize push delay values to default */
1849 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1851 if (!IS_CUSTOM_ELEMENT(i))
1854 /* set default push delay values (corrected since version 3.0.7-1) */
1855 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1857 element_info[i].push_delay_fixed = 2;
1858 element_info[i].push_delay_random = 8;
1862 element_info[i].push_delay_fixed = 8;
1863 element_info[i].push_delay_random = 8;
1866 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1867 element_info[i].push_delay_random = game.default_push_delay_random;
1872 /* set push delay value for certain elements from pre-defined list */
1873 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1875 int e = push_delay_list[i].element;
1877 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1878 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1881 /* set push delay value for Supaplex elements for newer engine versions */
1882 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1884 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1886 if (IS_SP_ELEMENT(i))
1888 /* set SP push delay to just enough to push under a falling zonk */
1889 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1891 element_info[i].push_delay_fixed = delay;
1892 element_info[i].push_delay_random = 0;
1897 /* ---------- initialize move stepsize ----------------------------------- */
1899 /* initialize move stepsize values to default */
1900 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1901 if (!IS_CUSTOM_ELEMENT(i))
1902 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1904 /* set move stepsize value for certain elements from pre-defined list */
1905 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1907 int e = move_stepsize_list[i].element;
1909 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1912 /* ---------- initialize collect score ----------------------------------- */
1914 /* initialize collect score values for custom elements from initial value */
1915 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1916 if (IS_CUSTOM_ELEMENT(i))
1917 element_info[i].collect_score = element_info[i].collect_score_initial;
1919 /* ---------- initialize collect count ----------------------------------- */
1921 /* initialize collect count values for non-custom elements */
1922 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1923 if (!IS_CUSTOM_ELEMENT(i))
1924 element_info[i].collect_count_initial = 0;
1926 /* add collect count values for all elements from pre-defined list */
1927 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1928 element_info[collect_count_list[i].element].collect_count_initial =
1929 collect_count_list[i].count;
1931 /* ---------- initialize access direction -------------------------------- */
1933 /* initialize access direction values to default (access from every side) */
1934 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1935 if (!IS_CUSTOM_ELEMENT(i))
1936 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1938 /* set access direction value for certain elements from pre-defined list */
1939 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1940 element_info[access_direction_list[i].element].access_direction =
1941 access_direction_list[i].direction;
1943 /* ---------- initialize explosion content ------------------------------- */
1944 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1946 if (IS_CUSTOM_ELEMENT(i))
1949 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1951 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1953 element_info[i].content.e[x][y] =
1954 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1955 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1956 i == EL_PLAYER_3 ? EL_EMERALD :
1957 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1958 i == EL_MOLE ? EL_EMERALD_RED :
1959 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1960 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1961 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1962 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1963 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1964 i == EL_WALL_EMERALD ? EL_EMERALD :
1965 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1966 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1967 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1968 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1969 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1970 i == EL_WALL_PEARL ? EL_PEARL :
1971 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1977 /* ---------- initialize reference elements ------------------------------- */
1978 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1980 int element = EL_CUSTOM_START + i;
1981 struct ElementInfo *ei = &element_info[element];
1983 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1984 replace_reference_element(element, &ei->content.e[x][y]);
1986 for (j = 0; j < ei->num_change_pages; j++)
1988 struct ElementChangeInfo *change = &ei->change_page[j];
1990 replace_reference_element(element, &change->target_element);
1991 replace_reference_element(element, &change->trigger_element);
1993 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1994 replace_reference_element(element, &change->target_content.e[x][y]);
2000 int get_num_special_action(int element, int action_first, int action_last)
2002 int num_special_action = 0;
2005 for (i = action_first; i <= action_last; i++)
2007 boolean found = FALSE;
2009 for (j = 0; j < NUM_DIRECTIONS; j++)
2010 if (el_act_dir2img(element, i, j) !=
2011 el_act_dir2img(element, ACTION_DEFAULT, j))
2015 num_special_action++;
2021 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
2024 return num_special_action;
2028 =============================================================================
2030 -----------------------------------------------------------------------------
2031 initialize and start new game
2032 =============================================================================
2037 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2038 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2039 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2040 boolean do_fading = (game_status == GAME_MODE_MAIN);
2043 game_status = GAME_MODE_PLAYING;
2047 /* don't play tapes over network */
2048 network_playing = (options.network && !tape.playing);
2050 for (i = 0; i < MAX_PLAYERS; i++)
2052 struct PlayerInfo *player = &stored_player[i];
2054 player->index_nr = i;
2055 player->index_bit = (1 << i);
2056 player->element_nr = EL_PLAYER_1 + i;
2058 player->present = FALSE;
2059 player->active = FALSE;
2062 player->effective_action = 0;
2063 player->programmed_action = 0;
2066 player->gems_still_needed = level.gems_needed;
2067 player->sokobanfields_still_needed = 0;
2068 player->lights_still_needed = 0;
2069 player->friends_still_needed = 0;
2071 for (j = 0; j < MAX_NUM_KEYS; j++)
2072 player->key[j] = FALSE;
2074 player->dynabomb_count = 0;
2075 player->dynabomb_size = 1;
2076 player->dynabombs_left = 0;
2077 player->dynabomb_xl = FALSE;
2079 player->MovDir = MV_NONE;
2082 player->GfxDir = MV_NONE;
2083 player->GfxAction = ACTION_DEFAULT;
2085 player->StepFrame = 0;
2087 player->use_murphy = FALSE;
2088 player->artwork_element =
2089 (level.use_artwork_element[i] ? level.artwork_element[i] :
2090 player->element_nr);
2092 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2093 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2095 player->gravity = level.initial_player_gravity[i];
2097 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2099 player->actual_frame_counter = 0;
2101 player->step_counter = 0;
2103 player->last_move_dir = MV_NONE;
2105 player->is_active = FALSE;
2107 player->is_waiting = FALSE;
2108 player->is_moving = FALSE;
2109 player->is_auto_moving = FALSE;
2110 player->is_digging = FALSE;
2111 player->is_snapping = FALSE;
2112 player->is_collecting = FALSE;
2113 player->is_pushing = FALSE;
2114 player->is_switching = FALSE;
2115 player->is_dropping = FALSE;
2116 player->is_dropping_pressed = FALSE;
2118 player->is_bored = FALSE;
2119 player->is_sleeping = FALSE;
2121 player->frame_counter_bored = -1;
2122 player->frame_counter_sleeping = -1;
2124 player->anim_delay_counter = 0;
2125 player->post_delay_counter = 0;
2127 player->dir_waiting = MV_NONE;
2128 player->action_waiting = ACTION_DEFAULT;
2129 player->last_action_waiting = ACTION_DEFAULT;
2130 player->special_action_bored = ACTION_DEFAULT;
2131 player->special_action_sleeping = ACTION_DEFAULT;
2134 /* cannot be set here -- could be modified in Init[Player]Field() below */
2136 /* set number of special actions for bored and sleeping animation */
2137 player->num_special_action_bored =
2138 get_num_special_action(player->artwork_element,
2139 ACTION_BORING_1, ACTION_BORING_LAST);
2140 player->num_special_action_sleeping =
2141 get_num_special_action(player->artwork_element,
2142 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2145 player->switch_x = -1;
2146 player->switch_y = -1;
2148 player->drop_x = -1;
2149 player->drop_y = -1;
2151 player->show_envelope = 0;
2154 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2156 player->move_delay = game.initial_move_delay;
2157 player->move_delay_value = game.initial_move_delay_value;
2159 player->move_delay_value_next = -1;
2161 player->move_delay_reset_counter = 0;
2163 player->cannot_move = FALSE;
2166 player->push_delay = -1; /* initialized when pushing starts */
2167 player->push_delay_value = game.initial_push_delay_value;
2169 player->drop_delay = 0;
2170 player->drop_pressed_delay = 0;
2172 player->last_jx = player->last_jy = 0;
2173 player->jx = player->jy = 0;
2175 player->shield_normal_time_left = 0;
2176 player->shield_deadly_time_left = 0;
2178 player->inventory_infinite_element = EL_UNDEFINED;
2179 player->inventory_size = 0;
2181 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2182 SnapField(player, 0, 0);
2184 player->LevelSolved = FALSE;
2185 player->GameOver = FALSE;
2187 player->LevelSolved_GameEnd = FALSE;
2188 player->LevelSolved_SaveTape = FALSE;
2189 player->LevelSolved_SaveScore = FALSE;
2192 network_player_action_received = FALSE;
2194 #if defined(NETWORK_AVALIABLE)
2195 /* initial null action */
2196 if (network_playing)
2197 SendToServer_MovePlayer(MV_NONE);
2206 TimeLeft = level.time;
2209 ScreenMovDir = MV_NONE;
2213 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2215 AllPlayersGone = FALSE;
2217 game.yamyam_content_nr = 0;
2218 game.magic_wall_active = FALSE;
2219 game.magic_wall_time_left = 0;
2220 game.light_time_left = 0;
2221 game.timegate_time_left = 0;
2222 game.switchgate_pos = 0;
2223 game.wind_direction = level.wind_direction_initial;
2225 #if !USE_PLAYER_GRAVITY
2227 game.gravity = FALSE;
2229 game.gravity = level.initial_gravity;
2231 game.explosions_delayed = TRUE;
2234 game.lenses_time_left = 0;
2235 game.magnify_time_left = 0;
2237 game.ball_state = level.ball_state_initial;
2238 game.ball_content_nr = 0;
2240 game.envelope_active = FALSE;
2242 /* set focus to local player for network games, else to all players */
2243 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2244 game.centered_player_nr_next = game.centered_player_nr;
2245 game.set_centered_player = FALSE;
2247 if (network_playing && tape.recording)
2249 /* store client dependent player focus when recording network games */
2250 tape.centered_player_nr_next = game.centered_player_nr_next;
2251 tape.set_centered_player = TRUE;
2255 printf("::: focus set to player %d [%d]\n",
2256 game.centered_player_nr, local_player->index_nr);
2259 for (i = 0; i < NUM_BELTS; i++)
2261 game.belt_dir[i] = MV_NONE;
2262 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2265 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2266 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2269 SCAN_PLAYFIELD(x, y)
2271 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2274 Feld[x][y] = level.field[x][y];
2275 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2276 ChangeDelay[x][y] = 0;
2277 ChangePage[x][y] = -1;
2278 #if USE_NEW_CUSTOM_VALUE
2279 CustomValue[x][y] = 0; /* initialized in InitField() */
2281 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2283 WasJustMoving[x][y] = 0;
2284 WasJustFalling[x][y] = 0;
2285 CheckCollision[x][y] = 0;
2287 Pushed[x][y] = FALSE;
2289 ChangeCount[x][y] = 0;
2290 ChangeEvent[x][y] = -1;
2292 ExplodePhase[x][y] = 0;
2293 ExplodeDelay[x][y] = 0;
2294 ExplodeField[x][y] = EX_TYPE_NONE;
2296 RunnerVisit[x][y] = 0;
2297 PlayerVisit[x][y] = 0;
2300 GfxRandom[x][y] = INIT_GFX_RANDOM();
2301 GfxElement[x][y] = EL_UNDEFINED;
2302 GfxAction[x][y] = ACTION_DEFAULT;
2303 GfxDir[x][y] = MV_NONE;
2307 SCAN_PLAYFIELD(x, y)
2309 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2312 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2314 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2316 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2319 InitField(x, y, TRUE);
2324 for (i = 0; i < MAX_PLAYERS; i++)
2326 struct PlayerInfo *player = &stored_player[i];
2329 /* set number of special actions for bored and sleeping animation */
2330 player->num_special_action_bored =
2331 get_num_special_action(player->artwork_element,
2332 ACTION_BORING_1, ACTION_BORING_LAST);
2333 player->num_special_action_sleeping =
2334 get_num_special_action(player->artwork_element,
2335 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2340 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2341 emulate_sb ? EMU_SOKOBAN :
2342 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2344 #if USE_NEW_ALL_SLIPPERY
2345 /* initialize type of slippery elements */
2346 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2348 if (!IS_CUSTOM_ELEMENT(i))
2350 /* default: elements slip down either to the left or right randomly */
2351 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2353 /* SP style elements prefer to slip down on the left side */
2354 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2355 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2357 /* BD style elements prefer to slip down on the left side */
2358 if (game.emulation == EMU_BOULDERDASH)
2359 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2364 /* initialize explosion and ignition delay */
2365 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2367 if (!IS_CUSTOM_ELEMENT(i))
2370 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2371 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2372 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2373 int last_phase = (num_phase + 1) * delay;
2374 int half_phase = (num_phase / 2) * delay;
2376 element_info[i].explosion_delay = last_phase - 1;
2377 element_info[i].ignition_delay = half_phase;
2379 if (i == EL_BLACK_ORB)
2380 element_info[i].ignition_delay = 1;
2384 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2385 element_info[i].explosion_delay = 1;
2387 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2388 element_info[i].ignition_delay = 1;
2392 /* correct non-moving belts to start moving left */
2393 for (i = 0; i < NUM_BELTS; i++)
2394 if (game.belt_dir[i] == MV_NONE)
2395 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2397 /* check if any connected player was not found in playfield */
2398 for (i = 0; i < MAX_PLAYERS; i++)
2400 struct PlayerInfo *player = &stored_player[i];
2402 if (player->connected && !player->present)
2404 for (j = 0; j < MAX_PLAYERS; j++)
2406 struct PlayerInfo *some_player = &stored_player[j];
2407 int jx = some_player->jx, jy = some_player->jy;
2409 /* assign first free player found that is present in the playfield */
2410 if (some_player->present && !some_player->connected)
2412 player->present = TRUE;
2413 player->active = TRUE;
2415 some_player->present = FALSE;
2416 some_player->active = FALSE;
2419 player->element_nr = some_player->element_nr;
2422 player->artwork_element = some_player->artwork_element;
2424 player->block_last_field = some_player->block_last_field;
2425 player->block_delay_adjustment = some_player->block_delay_adjustment;
2427 StorePlayer[jx][jy] = player->element_nr;
2428 player->jx = player->last_jx = jx;
2429 player->jy = player->last_jy = jy;
2439 /* when playing a tape, eliminate all players who do not participate */
2441 for (i = 0; i < MAX_PLAYERS; i++)
2443 if (stored_player[i].active && !tape.player_participates[i])
2445 struct PlayerInfo *player = &stored_player[i];
2446 int jx = player->jx, jy = player->jy;
2448 player->active = FALSE;
2449 StorePlayer[jx][jy] = 0;
2450 Feld[jx][jy] = EL_EMPTY;
2454 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2456 /* when in single player mode, eliminate all but the first active player */
2458 for (i = 0; i < MAX_PLAYERS; i++)
2460 if (stored_player[i].active)
2462 for (j = i + 1; j < MAX_PLAYERS; j++)
2464 if (stored_player[j].active)
2466 struct PlayerInfo *player = &stored_player[j];
2467 int jx = player->jx, jy = player->jy;
2469 player->active = FALSE;
2470 player->present = FALSE;
2472 StorePlayer[jx][jy] = 0;
2473 Feld[jx][jy] = EL_EMPTY;
2480 /* when recording the game, store which players take part in the game */
2483 for (i = 0; i < MAX_PLAYERS; i++)
2484 if (stored_player[i].active)
2485 tape.player_participates[i] = TRUE;
2490 for (i = 0; i < MAX_PLAYERS; i++)
2492 struct PlayerInfo *player = &stored_player[i];
2494 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2499 if (local_player == player)
2500 printf("Player %d is local player.\n", i+1);
2504 if (BorderElement == EL_EMPTY)
2507 SBX_Right = lev_fieldx - SCR_FIELDX;
2509 SBY_Lower = lev_fieldy - SCR_FIELDY;
2514 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2516 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2519 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2520 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2522 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2523 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2525 /* if local player not found, look for custom element that might create
2526 the player (make some assumptions about the right custom element) */
2527 if (!local_player->present)
2529 int start_x = 0, start_y = 0;
2530 int found_rating = 0;
2531 int found_element = EL_UNDEFINED;
2532 int player_nr = local_player->index_nr;
2535 SCAN_PLAYFIELD(x, y)
2537 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2540 int element = Feld[x][y];
2545 if (level.use_start_element[player_nr] &&
2546 level.start_element[player_nr] == element &&
2553 found_element = element;
2556 if (!IS_CUSTOM_ELEMENT(element))
2559 if (CAN_CHANGE(element))
2561 for (i = 0; i < element_info[element].num_change_pages; i++)
2563 /* check for player created from custom element as single target */
2564 content = element_info[element].change_page[i].target_element;
2565 is_player = ELEM_IS_PLAYER(content);
2567 if (is_player && (found_rating < 3 || element < found_element))
2573 found_element = element;
2578 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2580 /* check for player created from custom element as explosion content */
2581 content = element_info[element].content.e[xx][yy];
2582 is_player = ELEM_IS_PLAYER(content);
2584 if (is_player && (found_rating < 2 || element < found_element))
2586 start_x = x + xx - 1;
2587 start_y = y + yy - 1;
2590 found_element = element;
2593 if (!CAN_CHANGE(element))
2596 for (i = 0; i < element_info[element].num_change_pages; i++)
2598 /* check for player created from custom element as extended target */
2600 element_info[element].change_page[i].target_content.e[xx][yy];
2602 is_player = ELEM_IS_PLAYER(content);
2604 if (is_player && (found_rating < 1 || element < found_element))
2606 start_x = x + xx - 1;
2607 start_y = y + yy - 1;
2610 found_element = element;
2616 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2617 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2620 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2621 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2626 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2627 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2628 local_player->jx - MIDPOSX);
2630 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2631 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2632 local_player->jy - MIDPOSY);
2638 FadeOutField(TITLE_SCREEN_FADE_DELAY, TITLE_SCREEN_POST_DELAY);
2640 if (!game.restart_level)
2641 CloseDoor(DOOR_CLOSE_1);
2643 /* !!! FIX THIS (START) !!! */
2644 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2646 InitGameEngine_EM();
2653 /* after drawing the level, correct some elements */
2654 if (game.timegate_time_left == 0)
2655 CloseAllOpenTimegates();
2657 if (setup.soft_scrolling)
2658 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2660 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2666 /* !!! FIX THIS (END) !!! */
2669 FadeInField(TITLE_SCREEN_FADE_DELAY);
2671 if (!game.restart_level)
2673 /* copy default game door content to main double buffer */
2674 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2675 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2679 SetPanelBackground();
2680 SetDrawBackgroundMask(REDRAW_DOOR_1);
2683 DrawGameDoorValues();
2685 if (!game.restart_level)
2689 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2690 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2691 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2695 /* copy actual game door content to door double buffer for OpenDoor() */
2696 BlitBitmap(drawto, bitmap_db_door,
2697 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2699 OpenDoor(DOOR_OPEN_ALL);
2701 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2703 if (setup.sound_music)
2706 KeyboardAutoRepeatOffUnlessAutoplay();
2710 for (i = 0; i < MAX_PLAYERS; i++)
2711 printf("Player %d %sactive.\n",
2712 i + 1, (stored_player[i].active ? "" : "not "));
2716 game.restart_level = FALSE;
2719 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2721 /* this is used for non-R'n'D game engines to update certain engine values */
2723 /* needed to determine if sounds are played within the visible screen area */
2724 scroll_x = actual_scroll_x;
2725 scroll_y = actual_scroll_y;
2728 void InitMovDir(int x, int y)
2730 int i, element = Feld[x][y];
2731 static int xy[4][2] =
2738 static int direction[3][4] =
2740 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2741 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2742 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2751 Feld[x][y] = EL_BUG;
2752 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2755 case EL_SPACESHIP_RIGHT:
2756 case EL_SPACESHIP_UP:
2757 case EL_SPACESHIP_LEFT:
2758 case EL_SPACESHIP_DOWN:
2759 Feld[x][y] = EL_SPACESHIP;
2760 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2763 case EL_BD_BUTTERFLY_RIGHT:
2764 case EL_BD_BUTTERFLY_UP:
2765 case EL_BD_BUTTERFLY_LEFT:
2766 case EL_BD_BUTTERFLY_DOWN:
2767 Feld[x][y] = EL_BD_BUTTERFLY;
2768 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2771 case EL_BD_FIREFLY_RIGHT:
2772 case EL_BD_FIREFLY_UP:
2773 case EL_BD_FIREFLY_LEFT:
2774 case EL_BD_FIREFLY_DOWN:
2775 Feld[x][y] = EL_BD_FIREFLY;
2776 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2779 case EL_PACMAN_RIGHT:
2781 case EL_PACMAN_LEFT:
2782 case EL_PACMAN_DOWN:
2783 Feld[x][y] = EL_PACMAN;
2784 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2787 case EL_YAMYAM_LEFT:
2788 case EL_YAMYAM_RIGHT:
2790 case EL_YAMYAM_DOWN:
2791 Feld[x][y] = EL_YAMYAM;
2792 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2795 case EL_SP_SNIKSNAK:
2796 MovDir[x][y] = MV_UP;
2799 case EL_SP_ELECTRON:
2800 MovDir[x][y] = MV_LEFT;
2807 Feld[x][y] = EL_MOLE;
2808 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2812 if (IS_CUSTOM_ELEMENT(element))
2814 struct ElementInfo *ei = &element_info[element];
2815 int move_direction_initial = ei->move_direction_initial;
2816 int move_pattern = ei->move_pattern;
2818 if (move_direction_initial == MV_START_PREVIOUS)
2820 if (MovDir[x][y] != MV_NONE)
2823 move_direction_initial = MV_START_AUTOMATIC;
2826 if (move_direction_initial == MV_START_RANDOM)
2827 MovDir[x][y] = 1 << RND(4);
2828 else if (move_direction_initial & MV_ANY_DIRECTION)
2829 MovDir[x][y] = move_direction_initial;
2830 else if (move_pattern == MV_ALL_DIRECTIONS ||
2831 move_pattern == MV_TURNING_LEFT ||
2832 move_pattern == MV_TURNING_RIGHT ||
2833 move_pattern == MV_TURNING_LEFT_RIGHT ||
2834 move_pattern == MV_TURNING_RIGHT_LEFT ||
2835 move_pattern == MV_TURNING_RANDOM)
2836 MovDir[x][y] = 1 << RND(4);
2837 else if (move_pattern == MV_HORIZONTAL)
2838 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2839 else if (move_pattern == MV_VERTICAL)
2840 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2841 else if (move_pattern & MV_ANY_DIRECTION)
2842 MovDir[x][y] = element_info[element].move_pattern;
2843 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2844 move_pattern == MV_ALONG_RIGHT_SIDE)
2846 /* use random direction as default start direction */
2847 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2848 MovDir[x][y] = 1 << RND(4);
2850 for (i = 0; i < NUM_DIRECTIONS; i++)
2852 int x1 = x + xy[i][0];
2853 int y1 = y + xy[i][1];
2855 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2857 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2858 MovDir[x][y] = direction[0][i];
2860 MovDir[x][y] = direction[1][i];
2869 MovDir[x][y] = 1 << RND(4);
2871 if (element != EL_BUG &&
2872 element != EL_SPACESHIP &&
2873 element != EL_BD_BUTTERFLY &&
2874 element != EL_BD_FIREFLY)
2877 for (i = 0; i < NUM_DIRECTIONS; i++)
2879 int x1 = x + xy[i][0];
2880 int y1 = y + xy[i][1];
2882 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2884 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2886 MovDir[x][y] = direction[0][i];
2889 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2890 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2892 MovDir[x][y] = direction[1][i];
2901 GfxDir[x][y] = MovDir[x][y];
2904 void InitAmoebaNr(int x, int y)
2907 int group_nr = AmoebeNachbarNr(x, y);
2911 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2913 if (AmoebaCnt[i] == 0)
2921 AmoebaNr[x][y] = group_nr;
2922 AmoebaCnt[group_nr]++;
2923 AmoebaCnt2[group_nr]++;
2926 #if USE_NEW_GAME_WON
2930 static boolean score_done = FALSE;
2931 static boolean player_done = FALSE;
2932 static int game_over_delay = 0;
2933 int game_over_delay_value = 50;
2935 /* do not start end game actions before the player stops moving (to exit) */
2936 if (local_player->MovPos)
2939 if (tape.auto_play) /* tape might already be stopped here */
2940 tape.auto_play_level_solved = TRUE;
2942 if (!local_player->LevelSolved_GameEnd)
2944 local_player->LevelSolved_GameEnd = TRUE;
2945 local_player->LevelSolved_SaveTape = tape.recording;
2946 local_player->LevelSolved_SaveScore = !tape.playing;
2949 player_done = FALSE;
2950 game_over_delay = 0;
2953 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2959 if (setup.sound_loops)
2960 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2961 SND_CTRL_PLAY_LOOP);
2963 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2966 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2969 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2974 RaiseScore(level.score[SC_TIME_BONUS]);
2977 DrawGameValue_Time(TimeLeft);
2984 if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
2985 StopSound(SND_GAME_LEVELTIME_BONUS);
2987 else if (level.time == 0 && TimePlayed < 999) /* level without time limit */
2991 if (setup.sound_loops)
2992 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2993 SND_CTRL_PLAY_LOOP);
2995 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2998 if (TimePlayed < 900 && TimePlayed % 10 == 0)
3001 RaiseScore(level.score[SC_TIME_BONUS] * 10);
3006 RaiseScore(level.score[SC_TIME_BONUS]);
3009 DrawGameValue_Time(TimePlayed);
3011 if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
3012 StopSound(SND_GAME_LEVELTIME_BONUS);
3019 /* close exit door after last player */
3020 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3021 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3022 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3024 int element = Feld[ExitX][ExitY];
3026 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3027 EL_SP_EXIT_CLOSING);
3029 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3032 /* player disappears */
3033 if (ExitX >= 0 && ExitY >= 0 && !player_done)
3035 DrawLevelField(ExitX, ExitY);
3042 if (game_over_delay < game_over_delay_value || !score_done)
3049 boolean raise_level = FALSE;
3051 CloseDoor(DOOR_CLOSE_1);
3053 if (local_player->LevelSolved_SaveTape)
3057 SaveTape(tape.level_nr); /* Ask to save tape */
3060 if (!local_player->LevelSolved_SaveScore)
3062 FadeOutField(TITLE_SCREEN_FADE_DELAY, TITLE_SCREEN_POST_DELAY);
3064 game_status = GAME_MODE_MAIN;
3066 DrawMainMenuExt(TITLE_SCREEN_FADE_DELAY, REDRAW_FIELD);
3071 if (level_nr == leveldir_current->handicap_level)
3073 leveldir_current->handicap_level++;
3074 SaveLevelSetup_SeriesInfo();
3077 if (level_editor_test_game)
3078 local_player->score = -1; /* no highscore when playing from editor */
3079 else if (level_nr < leveldir_current->last_level)
3080 raise_level = TRUE; /* advance to next level */
3082 if ((hi_pos = NewHiScore()) >= 0)
3084 game_status = GAME_MODE_SCORES;
3086 DrawHallOfFame(hi_pos);
3096 FadeOutField(TITLE_SCREEN_FADE_DELAY, TITLE_SCREEN_POST_DELAY);
3098 game_status = GAME_MODE_MAIN;
3106 DrawMainMenuExt(TITLE_SCREEN_FADE_DELAY, REDRAW_FIELD);
3109 local_player->LevelSolved_SaveScore = FALSE;
3117 boolean raise_level = FALSE;
3119 if (local_player->MovPos)
3122 if (tape.auto_play) /* tape might already be stopped here */
3123 tape.auto_play_level_solved = TRUE;
3125 local_player->LevelSolved = FALSE;
3127 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
3131 if (!tape.playing && setup.sound_loops)
3132 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3133 SND_CTRL_PLAY_LOOP);
3135 while (TimeLeft > 0)
3137 if (!tape.playing && !setup.sound_loops)
3138 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3140 if (TimeLeft > 100 && TimeLeft % 10 == 0)
3143 RaiseScore(level.score[SC_TIME_BONUS] * 10);
3148 RaiseScore(level.score[SC_TIME_BONUS]);
3151 DrawGameValue_Time(TimeLeft);
3159 if (!tape.playing && setup.sound_loops)
3160 StopSound(SND_GAME_LEVELTIME_BONUS);
3162 else if (level.time == 0) /* level without time limit */
3164 if (!tape.playing && setup.sound_loops)
3165 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3166 SND_CTRL_PLAY_LOOP);
3168 while (TimePlayed < 999)
3170 if (!tape.playing && !setup.sound_loops)
3171 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3173 if (TimePlayed < 900 && TimePlayed % 10 == 0)
3176 RaiseScore(level.score[SC_TIME_BONUS] * 10);
3181 RaiseScore(level.score[SC_TIME_BONUS]);
3184 DrawGameValue_Time(TimePlayed);
3192 if (!tape.playing && setup.sound_loops)
3193 StopSound(SND_GAME_LEVELTIME_BONUS);
3196 /* close exit door after last player */
3197 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3198 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3199 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3201 int element = Feld[ExitX][ExitY];
3203 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3204 EL_SP_EXIT_CLOSING);
3206 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3209 /* player disappears */
3210 if (ExitX >= 0 && ExitY >= 0)
3211 DrawLevelField(ExitX, ExitY);
3217 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
3219 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
3225 CloseDoor(DOOR_CLOSE_1);
3230 SaveTape(tape.level_nr); /* Ask to save tape */
3233 if (level_nr == leveldir_current->handicap_level)
3235 leveldir_current->handicap_level++;
3236 SaveLevelSetup_SeriesInfo();
3239 if (level_editor_test_game)
3240 local_player->score = -1; /* no highscore when playing from editor */
3241 else if (level_nr < leveldir_current->last_level)
3242 raise_level = TRUE; /* advance to next level */
3244 if ((hi_pos = NewHiScore()) >= 0)
3246 game_status = GAME_MODE_SCORES;
3247 DrawHallOfFame(hi_pos);
3256 game_status = GAME_MODE_MAIN;
3275 LoadScore(level_nr);
3277 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3278 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3281 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3283 if (local_player->score > highscore[k].Score)
3285 /* player has made it to the hall of fame */
3287 if (k < MAX_SCORE_ENTRIES - 1)
3289 int m = MAX_SCORE_ENTRIES - 1;
3292 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3293 if (strEqual(setup.player_name, highscore[l].Name))
3295 if (m == k) /* player's new highscore overwrites his old one */
3299 for (l = m; l > k; l--)
3301 strcpy(highscore[l].Name, highscore[l - 1].Name);
3302 highscore[l].Score = highscore[l - 1].Score;
3309 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3310 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3311 highscore[k].Score = local_player->score;
3317 else if (!strncmp(setup.player_name, highscore[k].Name,
3318 MAX_PLAYER_NAME_LEN))
3319 break; /* player already there with a higher score */
3325 SaveScore(level_nr);
3330 inline static int getElementMoveStepsize(int x, int y)
3332 int element = Feld[x][y];
3333 int direction = MovDir[x][y];
3334 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3335 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3336 int horiz_move = (dx != 0);
3337 int sign = (horiz_move ? dx : dy);
3338 int step = sign * element_info[element].move_stepsize;
3340 /* special values for move stepsize for spring and things on conveyor belt */
3344 if (element == EL_SPRING)
3345 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3346 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3347 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3348 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3350 if (CAN_FALL(element) &&
3351 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3352 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3353 else if (element == EL_SPRING)
3354 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3361 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3363 if (player->GfxAction != action || player->GfxDir != dir)
3366 printf("Player frame reset! (%d => %d, %d => %d)\n",
3367 player->GfxAction, action, player->GfxDir, dir);
3370 player->GfxAction = action;
3371 player->GfxDir = dir;
3373 player->StepFrame = 0;
3377 #if USE_GFX_RESET_GFX_ANIMATION
3378 static void ResetGfxFrame(int x, int y, boolean redraw)
3380 int element = Feld[x][y];
3381 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3382 int last_gfx_frame = GfxFrame[x][y];
3384 if (graphic_info[graphic].anim_global_sync)
3385 GfxFrame[x][y] = FrameCounter;
3386 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3387 GfxFrame[x][y] = CustomValue[x][y];
3388 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3389 GfxFrame[x][y] = element_info[element].collect_score;
3390 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3391 GfxFrame[x][y] = ChangeDelay[x][y];
3393 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3394 DrawLevelGraphicAnimation(x, y, graphic);
3398 static void ResetGfxAnimation(int x, int y)
3401 int element, graphic;
3404 GfxAction[x][y] = ACTION_DEFAULT;
3405 GfxDir[x][y] = MovDir[x][y];
3409 element = Feld[x][y];
3410 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3412 if (graphic_info[graphic].anim_global_sync)
3413 GfxFrame[x][y] = FrameCounter;
3414 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3415 GfxFrame[x][y] = CustomValue[x][y];
3416 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3417 GfxFrame[x][y] = element_info[element].collect_score;
3418 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3419 GfxFrame[x][y] = ChangeDelay[x][y];
3422 #if USE_GFX_RESET_GFX_ANIMATION
3423 ResetGfxFrame(x, y, FALSE);
3427 static void ResetRandomAnimationValue(int x, int y)
3429 GfxRandom[x][y] = INIT_GFX_RANDOM();
3432 void InitMovingField(int x, int y, int direction)
3434 int element = Feld[x][y];
3438 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3439 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3443 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3444 ResetGfxAnimation(x, y);
3446 MovDir[x][y] = direction;
3447 GfxDir[x][y] = direction;
3448 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3449 ACTION_FALLING : ACTION_MOVING);
3452 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3454 if (graphic_info[graphic].anim_global_sync)
3455 GfxFrame[x][y] = FrameCounter;
3456 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3457 GfxFrame[x][y] = CustomValue[x][y];
3458 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3459 GfxFrame[x][y] = element_info[element].collect_score;
3460 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3461 GfxFrame[x][y] = ChangeDelay[x][y];
3464 /* this is needed for CEs with property "can move" / "not moving" */
3466 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3468 if (Feld[newx][newy] == EL_EMPTY)
3469 Feld[newx][newy] = EL_BLOCKED;
3471 MovDir[newx][newy] = MovDir[x][y];
3473 #if USE_NEW_CUSTOM_VALUE
3474 CustomValue[newx][newy] = CustomValue[x][y];
3477 GfxFrame[newx][newy] = GfxFrame[x][y];
3478 GfxRandom[newx][newy] = GfxRandom[x][y];
3479 GfxAction[newx][newy] = GfxAction[x][y];
3480 GfxDir[newx][newy] = GfxDir[x][y];
3484 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3486 int direction = MovDir[x][y];
3488 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3489 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3491 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3492 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3499 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3501 int oldx = x, oldy = y;
3502 int direction = MovDir[x][y];
3504 if (direction == MV_LEFT)
3506 else if (direction == MV_RIGHT)
3508 else if (direction == MV_UP)
3510 else if (direction == MV_DOWN)
3513 *comes_from_x = oldx;
3514 *comes_from_y = oldy;
3517 int MovingOrBlocked2Element(int x, int y)
3519 int element = Feld[x][y];
3521 if (element == EL_BLOCKED)
3525 Blocked2Moving(x, y, &oldx, &oldy);
3526 return Feld[oldx][oldy];
3532 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3534 /* like MovingOrBlocked2Element(), but if element is moving
3535 and (x,y) is the field the moving element is just leaving,
3536 return EL_BLOCKED instead of the element value */
3537 int element = Feld[x][y];
3539 if (IS_MOVING(x, y))
3541 if (element == EL_BLOCKED)
3545 Blocked2Moving(x, y, &oldx, &oldy);
3546 return Feld[oldx][oldy];
3555 static void RemoveField(int x, int y)
3557 Feld[x][y] = EL_EMPTY;
3563 #if USE_NEW_CUSTOM_VALUE
3564 CustomValue[x][y] = 0;
3568 ChangeDelay[x][y] = 0;
3569 ChangePage[x][y] = -1;
3570 Pushed[x][y] = FALSE;
3573 ExplodeField[x][y] = EX_TYPE_NONE;
3576 GfxElement[x][y] = EL_UNDEFINED;
3577 GfxAction[x][y] = ACTION_DEFAULT;
3578 GfxDir[x][y] = MV_NONE;
3581 void RemoveMovingField(int x, int y)
3583 int oldx = x, oldy = y, newx = x, newy = y;
3584 int element = Feld[x][y];
3585 int next_element = EL_UNDEFINED;
3587 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3590 if (IS_MOVING(x, y))
3592 Moving2Blocked(x, y, &newx, &newy);
3594 if (Feld[newx][newy] != EL_BLOCKED)
3596 /* element is moving, but target field is not free (blocked), but
3597 already occupied by something different (example: acid pool);
3598 in this case, only remove the moving field, but not the target */
3600 RemoveField(oldx, oldy);
3602 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3604 DrawLevelField(oldx, oldy);
3609 else if (element == EL_BLOCKED)
3611 Blocked2Moving(x, y, &oldx, &oldy);
3612 if (!IS_MOVING(oldx, oldy))
3616 if (element == EL_BLOCKED &&
3617 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3618 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3619 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3620 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3621 next_element = get_next_element(Feld[oldx][oldy]);
3623 RemoveField(oldx, oldy);
3624 RemoveField(newx, newy);
3626 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3628 if (next_element != EL_UNDEFINED)
3629 Feld[oldx][oldy] = next_element;
3631 DrawLevelField(oldx, oldy);
3632 DrawLevelField(newx, newy);
3635 void DrawDynamite(int x, int y)
3637 int sx = SCREENX(x), sy = SCREENY(y);
3638 int graphic = el2img(Feld[x][y]);
3641 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3644 if (IS_WALKABLE_INSIDE(Back[x][y]))
3648 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3649 else if (Store[x][y])
3650 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3652 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3654 if (Back[x][y] || Store[x][y])
3655 DrawGraphicThruMask(sx, sy, graphic, frame);
3657 DrawGraphic(sx, sy, graphic, frame);
3660 void CheckDynamite(int x, int y)
3662 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3666 if (MovDelay[x][y] != 0)
3669 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3675 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3682 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3684 boolean num_checked_players = 0;
3687 for (i = 0; i < MAX_PLAYERS; i++)
3689 if (stored_player[i].active)
3691 int sx = stored_player[i].jx;
3692 int sy = stored_player[i].jy;
3694 if (num_checked_players == 0)
3701 *sx1 = MIN(*sx1, sx);
3702 *sy1 = MIN(*sy1, sy);
3703 *sx2 = MAX(*sx2, sx);
3704 *sy2 = MAX(*sy2, sy);
3707 num_checked_players++;
3712 static boolean checkIfAllPlayersFitToScreen_RND()
3714 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3716 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3718 return (sx2 - sx1 < SCR_FIELDX &&
3719 sy2 - sy1 < SCR_FIELDY);
3722 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3724 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3726 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3728 *sx = (sx1 + sx2) / 2;
3729 *sy = (sy1 + sy2) / 2;
3733 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3734 int center_x, int center_y)
3736 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3738 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3740 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3741 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3744 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3748 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3750 return (max_dx <= SCR_FIELDX / 2 &&
3751 max_dy <= SCR_FIELDY / 2);
3759 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3760 boolean quick_relocation)
3762 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3763 boolean no_delay = (tape.warp_forward);
3764 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3765 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3767 if (quick_relocation)
3769 int offset = (setup.scroll_delay ? 3 : 0);
3776 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3778 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3779 x > SBX_Right + MIDPOSX ? SBX_Right :
3782 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3783 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3788 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3789 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3790 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3792 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3793 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3794 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3796 /* don't scroll over playfield boundaries */
3797 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3798 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3800 /* don't scroll over playfield boundaries */
3801 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3802 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3805 RedrawPlayfield(TRUE, 0,0,0,0);
3809 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3810 x > SBX_Right + MIDPOSX ? SBX_Right :
3813 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3814 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3817 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3819 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3822 int fx = FX, fy = FY;
3824 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3825 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3827 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3833 fx += dx * TILEX / 2;
3834 fy += dy * TILEY / 2;
3836 ScrollLevel(dx, dy);
3839 /* scroll in two steps of half tile size to make things smoother */
3840 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3842 Delay(wait_delay_value);
3844 /* scroll second step to align at full tile size */
3846 Delay(wait_delay_value);
3851 Delay(wait_delay_value);
3857 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3859 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3860 boolean no_delay = (tape.warp_forward);
3861 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3862 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3863 int jx = player->jx;
3864 int jy = player->jy;
3866 if (quick_relocation)
3868 int offset = (setup.scroll_delay ? 3 : 0);
3870 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3872 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3873 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3874 player->jx - MIDPOSX);
3876 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3877 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3878 player->jy - MIDPOSY);
3882 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3883 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3884 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3886 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3887 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3888 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3890 /* don't scroll over playfield boundaries */
3891 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3892 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3894 /* don't scroll over playfield boundaries */
3895 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3896 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3899 RedrawPlayfield(TRUE, 0,0,0,0);
3903 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3904 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3905 player->jx - MIDPOSX);
3907 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3908 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3909 player->jy - MIDPOSY);
3911 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3913 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3916 int fx = FX, fy = FY;
3918 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3919 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3921 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3927 fx += dx * TILEX / 2;
3928 fy += dy * TILEY / 2;
3930 ScrollLevel(dx, dy);
3933 /* scroll in two steps of half tile size to make things smoother */
3934 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3936 Delay(wait_delay_value);
3938 /* scroll second step to align at full tile size */
3940 Delay(wait_delay_value);
3945 Delay(wait_delay_value);
3951 void RelocatePlayer(int jx, int jy, int el_player_raw)
3953 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3954 int player_nr = GET_PLAYER_NR(el_player);
3955 struct PlayerInfo *player = &stored_player[player_nr];
3956 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3957 boolean no_delay = (tape.warp_forward);
3958 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3959 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3960 int old_jx = player->jx;
3961 int old_jy = player->jy;
3962 int old_element = Feld[old_jx][old_jy];
3963 int element = Feld[jx][jy];
3964 boolean player_relocated = (old_jx != jx || old_jy != jy);
3966 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3967 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3968 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3969 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3970 int leave_side_horiz = move_dir_horiz;
3971 int leave_side_vert = move_dir_vert;
3972 int enter_side = enter_side_horiz | enter_side_vert;
3973 int leave_side = leave_side_horiz | leave_side_vert;
3975 if (player->GameOver) /* do not reanimate dead player */
3978 if (!player_relocated) /* no need to relocate the player */
3981 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3983 RemoveField(jx, jy); /* temporarily remove newly placed player */
3984 DrawLevelField(jx, jy);
3987 if (player->present)
3989 while (player->MovPos)
3991 ScrollPlayer(player, SCROLL_GO_ON);
3992 ScrollScreen(NULL, SCROLL_GO_ON);
3994 AdvanceFrameAndPlayerCounters(player->index_nr);
3999 Delay(wait_delay_value);
4002 DrawPlayer(player); /* needed here only to cleanup last field */
4003 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4005 player->is_moving = FALSE;
4008 if (IS_CUSTOM_ELEMENT(old_element))
4009 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4011 player->index_bit, leave_side);
4013 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4015 player->index_bit, leave_side);
4017 Feld[jx][jy] = el_player;
4018 InitPlayerField(jx, jy, el_player, TRUE);
4020 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4022 Feld[jx][jy] = element;
4023 InitField(jx, jy, FALSE);
4027 /* only visually relocate centered player */
4029 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
4030 level.instant_relocation);
4032 if (player->index_nr == game.centered_player_nr)
4033 DrawRelocatePlayer(player, level.instant_relocation);
4036 if (player == local_player) /* only visually relocate local player */
4037 DrawRelocatePlayer(player, level.instant_relocation);
4040 TestIfPlayerTouchesBadThing(jx, jy);
4041 TestIfPlayerTouchesCustomElement(jx, jy);
4043 if (IS_CUSTOM_ELEMENT(element))
4044 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4045 player->index_bit, enter_side);
4047 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4048 player->index_bit, enter_side);
4051 void Explode(int ex, int ey, int phase, int mode)
4057 /* !!! eliminate this variable !!! */
4058 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4060 if (game.explosions_delayed)
4062 ExplodeField[ex][ey] = mode;
4066 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4068 int center_element = Feld[ex][ey];
4069 int artwork_element, explosion_element; /* set these values later */
4072 /* --- This is only really needed (and now handled) in "Impact()". --- */
4073 /* do not explode moving elements that left the explode field in time */
4074 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4075 center_element == EL_EMPTY &&
4076 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4081 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4082 if (mode == EX_TYPE_NORMAL ||
4083 mode == EX_TYPE_CENTER ||
4084 mode == EX_TYPE_CROSS)
4085 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4088 /* remove things displayed in background while burning dynamite */
4089 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4092 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4094 /* put moving element to center field (and let it explode there) */
4095 center_element = MovingOrBlocked2Element(ex, ey);
4096 RemoveMovingField(ex, ey);
4097 Feld[ex][ey] = center_element;
4100 /* now "center_element" is finally determined -- set related values now */
4101 artwork_element = center_element; /* for custom player artwork */
4102 explosion_element = center_element; /* for custom player artwork */
4104 if (IS_PLAYER(ex, ey))
4106 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4108 artwork_element = stored_player[player_nr].artwork_element;
4110 if (level.use_explosion_element[player_nr])
4112 explosion_element = level.explosion_element[player_nr];
4113 artwork_element = explosion_element;
4118 if (mode == EX_TYPE_NORMAL ||
4119 mode == EX_TYPE_CENTER ||
4120 mode == EX_TYPE_CROSS)
4121 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4125 last_phase = element_info[explosion_element].explosion_delay + 1;
4127 last_phase = element_info[center_element].explosion_delay + 1;
4130 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4132 int xx = x - ex + 1;
4133 int yy = y - ey + 1;
4136 if (!IN_LEV_FIELD(x, y) ||
4137 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4138 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4141 element = Feld[x][y];
4143 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4145 element = MovingOrBlocked2Element(x, y);
4147 if (!IS_EXPLOSION_PROOF(element))
4148 RemoveMovingField(x, y);
4151 /* indestructible elements can only explode in center (but not flames) */
4152 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4153 mode == EX_TYPE_BORDER)) ||
4154 element == EL_FLAMES)
4157 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4158 behaviour, for example when touching a yamyam that explodes to rocks
4159 with active deadly shield, a rock is created under the player !!! */
4160 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4162 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4163 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4164 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4166 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4169 if (IS_ACTIVE_BOMB(element))
4171 /* re-activate things under the bomb like gate or penguin */
4172 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4179 /* save walkable background elements while explosion on same tile */
4180 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4181 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4182 Back[x][y] = element;
4184 /* ignite explodable elements reached by other explosion */
4185 if (element == EL_EXPLOSION)
4186 element = Store2[x][y];
4188 if (AmoebaNr[x][y] &&
4189 (element == EL_AMOEBA_FULL ||
4190 element == EL_BD_AMOEBA ||
4191 element == EL_AMOEBA_GROWING))
4193 AmoebaCnt[AmoebaNr[x][y]]--;
4194 AmoebaCnt2[AmoebaNr[x][y]]--;
4199 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4202 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4204 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4206 switch(StorePlayer[ex][ey])
4209 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
4212 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
4215 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
4219 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
4224 if (PLAYERINFO(ex, ey)->use_murphy)
4225 Store[x][y] = EL_EMPTY;
4228 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4229 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4230 else if (ELEM_IS_PLAYER(center_element))
4231 Store[x][y] = EL_EMPTY;
4232 else if (center_element == EL_YAMYAM)
4233 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4234 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4235 Store[x][y] = element_info[center_element].content.e[xx][yy];
4237 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4238 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4239 otherwise) -- FIX THIS !!! */
4240 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4241 Store[x][y] = element_info[element].content.e[1][1];
4243 else if (!CAN_EXPLODE(element))
4244 Store[x][y] = element_info[element].content.e[1][1];
4247 Store[x][y] = EL_EMPTY;
4249 else if (center_element == EL_MOLE)
4250 Store[x][y] = EL_EMERALD_RED;
4251 else if (center_element == EL_PENGUIN)
4252 Store[x][y] = EL_EMERALD_PURPLE;
4253 else if (center_element == EL_BUG)
4254 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
4255 else if (center_element == EL_BD_BUTTERFLY)
4256 Store[x][y] = EL_BD_DIAMOND;
4257 else if (center_element == EL_SP_ELECTRON)
4258 Store[x][y] = EL_SP_INFOTRON;
4259 else if (center_element == EL_AMOEBA_TO_DIAMOND)
4260 Store[x][y] = level.amoeba_content;
4261 else if (center_element == EL_YAMYAM)
4262 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4263 else if (IS_CUSTOM_ELEMENT(center_element) &&
4264 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4265 Store[x][y] = element_info[center_element].content.e[xx][yy];
4266 else if (element == EL_WALL_EMERALD)
4267 Store[x][y] = EL_EMERALD;
4268 else if (element == EL_WALL_DIAMOND)
4269 Store[x][y] = EL_DIAMOND;
4270 else if (element == EL_WALL_BD_DIAMOND)
4271 Store[x][y] = EL_BD_DIAMOND;
4272 else if (element == EL_WALL_EMERALD_YELLOW)
4273 Store[x][y] = EL_EMERALD_YELLOW;
4274 else if (element == EL_WALL_EMERALD_RED)
4275 Store[x][y] = EL_EMERALD_RED;
4276 else if (element == EL_WALL_EMERALD_PURPLE)
4277 Store[x][y] = EL_EMERALD_PURPLE;
4278 else if (element == EL_WALL_PEARL)
4279 Store[x][y] = EL_PEARL;
4280 else if (element == EL_WALL_CRYSTAL)
4281 Store[x][y] = EL_CRYSTAL;
4282 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
4283 Store[x][y] = element_info[element].content.e[1][1];
4285 Store[x][y] = EL_EMPTY;
4288 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4289 center_element == EL_AMOEBA_TO_DIAMOND)
4290 Store2[x][y] = element;
4292 Feld[x][y] = EL_EXPLOSION;
4293 GfxElement[x][y] = artwork_element;
4296 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
4297 x, y, artwork_element, EL_NAME(artwork_element));
4300 ExplodePhase[x][y] = 1;
4301 ExplodeDelay[x][y] = last_phase;
4306 if (center_element == EL_YAMYAM)
4307 game.yamyam_content_nr =
4308 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4320 GfxFrame[x][y] = 0; /* restart explosion animation */
4322 last_phase = ExplodeDelay[x][y];
4324 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4328 /* activate this even in non-DEBUG version until cause for crash in
4329 getGraphicAnimationFrame() (see below) is found and eliminated */
4335 /* this can happen if the player leaves an explosion just in time */
4336 if (GfxElement[x][y] == EL_UNDEFINED)
4337 GfxElement[x][y] = EL_EMPTY;
4339 if (GfxElement[x][y] == EL_UNDEFINED)
4342 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4343 printf("Explode(): This should never happen!\n");
4346 GfxElement[x][y] = EL_EMPTY;
4352 border_element = Store2[x][y];
4353 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4354 border_element = StorePlayer[x][y];
4356 if (phase == element_info[border_element].ignition_delay ||
4357 phase == last_phase)
4359 boolean border_explosion = FALSE;
4361 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4362 !PLAYER_EXPLOSION_PROTECTED(x, y))
4364 KillPlayerUnlessExplosionProtected(x, y);
4365 border_explosion = TRUE;
4367 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4369 Feld[x][y] = Store2[x][y];
4372 border_explosion = TRUE;
4374 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4376 AmoebeUmwandeln(x, y);
4378 border_explosion = TRUE;
4381 /* if an element just explodes due to another explosion (chain-reaction),
4382 do not immediately end the new explosion when it was the last frame of
4383 the explosion (as it would be done in the following "if"-statement!) */
4384 if (border_explosion && phase == last_phase)
4388 if (phase == last_phase)
4392 element = Feld[x][y] = Store[x][y];
4393 Store[x][y] = Store2[x][y] = 0;
4394 GfxElement[x][y] = EL_UNDEFINED;
4396 /* player can escape from explosions and might therefore be still alive */
4397 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4398 element <= EL_PLAYER_IS_EXPLODING_4)
4400 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4401 int explosion_element = EL_PLAYER_1 + player_nr;
4402 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4403 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4405 if (level.use_explosion_element[player_nr])
4406 explosion_element = level.explosion_element[player_nr];
4408 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4409 element_info[explosion_element].content.e[xx][yy]);
4412 /* restore probably existing indestructible background element */
4413 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4414 element = Feld[x][y] = Back[x][y];
4417 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4418 GfxDir[x][y] = MV_NONE;
4419 ChangeDelay[x][y] = 0;
4420 ChangePage[x][y] = -1;
4422 #if USE_NEW_CUSTOM_VALUE
4423 CustomValue[x][y] = 0;
4426 InitField_WithBug2(x, y, FALSE);
4428 DrawLevelField(x, y);
4430 TestIfElementTouchesCustomElement(x, y);
4432 if (GFX_CRUMBLED(element))
4433 DrawLevelFieldCrumbledSandNeighbours(x, y);
4435 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4436 StorePlayer[x][y] = 0;
4438 if (ELEM_IS_PLAYER(element))
4439 RelocatePlayer(x, y, element);
4441 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4443 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4444 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4447 DrawLevelFieldCrumbledSand(x, y);
4449 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4451 DrawLevelElement(x, y, Back[x][y]);
4452 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4454 else if (IS_WALKABLE_UNDER(Back[x][y]))
4456 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4457 DrawLevelElementThruMask(x, y, Back[x][y]);
4459 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4460 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4464 void DynaExplode(int ex, int ey)
4467 int dynabomb_element = Feld[ex][ey];
4468 int dynabomb_size = 1;
4469 boolean dynabomb_xl = FALSE;
4470 struct PlayerInfo *player;
4471 static int xy[4][2] =
4479 if (IS_ACTIVE_BOMB(dynabomb_element))
4481 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4482 dynabomb_size = player->dynabomb_size;
4483 dynabomb_xl = player->dynabomb_xl;
4484 player->dynabombs_left++;
4487 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4489 for (i = 0; i < NUM_DIRECTIONS; i++)
4491 for (j = 1; j <= dynabomb_size; j++)
4493 int x = ex + j * xy[i][0];
4494 int y = ey + j * xy[i][1];
4497 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4500 element = Feld[x][y];
4502 /* do not restart explosions of fields with active bombs */
4503 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4506 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4508 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4509 !IS_DIGGABLE(element) && !dynabomb_xl)
4515 void Bang(int x, int y)
4517 int element = MovingOrBlocked2Element(x, y);
4518 int explosion_type = EX_TYPE_NORMAL;
4520 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4522 struct PlayerInfo *player = PLAYERINFO(x, y);
4524 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4525 player->element_nr);
4527 if (level.use_explosion_element[player->index_nr])
4529 int explosion_element = level.explosion_element[player->index_nr];
4531 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4532 explosion_type = EX_TYPE_CROSS;
4533 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4534 explosion_type = EX_TYPE_CENTER;
4542 case EL_BD_BUTTERFLY:
4545 case EL_DARK_YAMYAM:
4549 RaiseScoreElement(element);
4552 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4553 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4554 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4555 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4556 case EL_DYNABOMB_INCREASE_NUMBER:
4557 case EL_DYNABOMB_INCREASE_SIZE:
4558 case EL_DYNABOMB_INCREASE_POWER:
4559 explosion_type = EX_TYPE_DYNA;
4564 case EL_LAMP_ACTIVE:
4565 case EL_AMOEBA_TO_DIAMOND:
4566 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4567 explosion_type = EX_TYPE_CENTER;
4571 if (element_info[element].explosion_type == EXPLODES_CROSS)
4572 explosion_type = EX_TYPE_CROSS;
4573 else if (element_info[element].explosion_type == EXPLODES_1X1)
4574 explosion_type = EX_TYPE_CENTER;
4578 if (explosion_type == EX_TYPE_DYNA)
4581 Explode(x, y, EX_PHASE_START, explosion_type);
4583 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4586 void SplashAcid(int x, int y)
4588 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4589 (!IN_LEV_FIELD(x - 1, y - 2) ||
4590 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4591 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4593 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4594 (!IN_LEV_FIELD(x + 1, y - 2) ||
4595 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4596 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4598 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4601 static void InitBeltMovement()
4603 static int belt_base_element[4] =
4605 EL_CONVEYOR_BELT_1_LEFT,
4606 EL_CONVEYOR_BELT_2_LEFT,
4607 EL_CONVEYOR_BELT_3_LEFT,
4608 EL_CONVEYOR_BELT_4_LEFT
4610 static int belt_base_active_element[4] =
4612 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4613 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4614 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4615 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4620 /* set frame order for belt animation graphic according to belt direction */
4621 for (i = 0; i < NUM_BELTS; i++)
4625 for (j = 0; j < NUM_BELT_PARTS; j++)
4627 int element = belt_base_active_element[belt_nr] + j;
4628 int graphic = el2img(element);
4630 if (game.belt_dir[i] == MV_LEFT)
4631 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4633 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4638 SCAN_PLAYFIELD(x, y)
4640 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4643 int element = Feld[x][y];
4645 for (i = 0; i < NUM_BELTS; i++)
4647 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4649 int e_belt_nr = getBeltNrFromBeltElement(element);
4652 if (e_belt_nr == belt_nr)
4654 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4656 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4663 static void ToggleBeltSwitch(int x, int y)
4665 static int belt_base_element[4] =
4667 EL_CONVEYOR_BELT_1_LEFT,
4668 EL_CONVEYOR_BELT_2_LEFT,
4669 EL_CONVEYOR_BELT_3_LEFT,
4670 EL_CONVEYOR_BELT_4_LEFT
4672 static int belt_base_active_element[4] =
4674 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4675 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4676 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4677 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4679 static int belt_base_switch_element[4] =
4681 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4682 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4683 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4684 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4686 static int belt_move_dir[4] =
4694 int element = Feld[x][y];
4695 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4696 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4697 int belt_dir = belt_move_dir[belt_dir_nr];
4700 if (!IS_BELT_SWITCH(element))
4703 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4704 game.belt_dir[belt_nr] = belt_dir;
4706 if (belt_dir_nr == 3)
4709 /* set frame order for belt animation graphic according to belt direction */
4710 for (i = 0; i < NUM_BELT_PARTS; i++)
4712 int element = belt_base_active_element[belt_nr] + i;
4713 int graphic = el2img(element);
4715 if (belt_dir == MV_LEFT)
4716 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4718 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4722 SCAN_PLAYFIELD(xx, yy)
4724 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4727 int element = Feld[xx][yy];
4729 if (IS_BELT_SWITCH(element))
4731 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4733 if (e_belt_nr == belt_nr)
4735 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4736 DrawLevelField(xx, yy);
4739 else if (IS_BELT(element) && belt_dir != MV_NONE)
4741 int e_belt_nr = getBeltNrFromBeltElement(element);
4743 if (e_belt_nr == belt_nr)
4745 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4747 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4748 DrawLevelField(xx, yy);
4751 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4753 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4755 if (e_belt_nr == belt_nr)
4757 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4759 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4760 DrawLevelField(xx, yy);
4766 static void ToggleSwitchgateSwitch(int x, int y)
4770 game.switchgate_pos = !game.switchgate_pos;
4773 SCAN_PLAYFIELD(xx, yy)
4775 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4778 int element = Feld[xx][yy];
4780 #if !USE_BOTH_SWITCHGATE_SWITCHES
4781 if (element == EL_SWITCHGATE_SWITCH_UP ||
4782 element == EL_SWITCHGATE_SWITCH_DOWN)
4784 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4785 DrawLevelField(xx, yy);
4788 if (element == EL_SWITCHGATE_SWITCH_UP)
4790 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4791 DrawLevelField(xx, yy);
4793 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4795 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4796 DrawLevelField(xx, yy);
4799 else if (element == EL_SWITCHGATE_OPEN ||
4800 element == EL_SWITCHGATE_OPENING)
4802 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4804 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4806 else if (element == EL_SWITCHGATE_CLOSED ||
4807 element == EL_SWITCHGATE_CLOSING)
4809 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4811 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4816 static int getInvisibleActiveFromInvisibleElement(int element)
4818 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4819 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4820 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4824 static int getInvisibleFromInvisibleActiveElement(int element)
4826 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4827 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4828 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4832 static void RedrawAllLightSwitchesAndInvisibleElements()
4837 SCAN_PLAYFIELD(x, y)
4839 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4842 int element = Feld[x][y];
4844 if (element == EL_LIGHT_SWITCH &&
4845 game.light_time_left > 0)
4847 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4848 DrawLevelField(x, y);
4850 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4851 game.light_time_left == 0)
4853 Feld[x][y] = EL_LIGHT_SWITCH;
4854 DrawLevelField(x, y);
4856 else if (element == EL_EMC_DRIPPER &&
4857 game.light_time_left > 0)
4859 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4860 DrawLevelField(x, y);
4862 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4863 game.light_time_left == 0)
4865 Feld[x][y] = EL_EMC_DRIPPER;
4866 DrawLevelField(x, y);
4868 else if (element == EL_INVISIBLE_STEELWALL ||
4869 element == EL_INVISIBLE_WALL ||
4870 element == EL_INVISIBLE_SAND)
4872 if (game.light_time_left > 0)
4873 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4875 DrawLevelField(x, y);
4877 /* uncrumble neighbour fields, if needed */
4878 if (element == EL_INVISIBLE_SAND)
4879 DrawLevelFieldCrumbledSandNeighbours(x, y);
4881 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4882 element == EL_INVISIBLE_WALL_ACTIVE ||
4883 element == EL_INVISIBLE_SAND_ACTIVE)
4885 if (game.light_time_left == 0)
4886 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4888 DrawLevelField(x, y);
4890 /* re-crumble neighbour fields, if needed */
4891 if (element == EL_INVISIBLE_SAND)
4892 DrawLevelFieldCrumbledSandNeighbours(x, y);
4897 static void RedrawAllInvisibleElementsForLenses()
4902 SCAN_PLAYFIELD(x, y)
4904 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4907 int element = Feld[x][y];
4909 if (element == EL_EMC_DRIPPER &&
4910 game.lenses_time_left > 0)
4912 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4913 DrawLevelField(x, y);
4915 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4916 game.lenses_time_left == 0)
4918 Feld[x][y] = EL_EMC_DRIPPER;
4919 DrawLevelField(x, y);
4921 else if (element == EL_INVISIBLE_STEELWALL ||
4922 element == EL_INVISIBLE_WALL ||
4923 element == EL_INVISIBLE_SAND)
4925 if (game.lenses_time_left > 0)
4926 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4928 DrawLevelField(x, y);
4930 /* uncrumble neighbour fields, if needed */
4931 if (element == EL_INVISIBLE_SAND)
4932 DrawLevelFieldCrumbledSandNeighbours(x, y);
4934 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4935 element == EL_INVISIBLE_WALL_ACTIVE ||
4936 element == EL_INVISIBLE_SAND_ACTIVE)
4938 if (game.lenses_time_left == 0)
4939 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4941 DrawLevelField(x, y);
4943 /* re-crumble neighbour fields, if needed */
4944 if (element == EL_INVISIBLE_SAND)
4945 DrawLevelFieldCrumbledSandNeighbours(x, y);
4950 static void RedrawAllInvisibleElementsForMagnifier()
4955 SCAN_PLAYFIELD(x, y)
4957 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4960 int element = Feld[x][y];
4962 if (element == EL_EMC_FAKE_GRASS &&
4963 game.magnify_time_left > 0)
4965 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4966 DrawLevelField(x, y);
4968 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4969 game.magnify_time_left == 0)
4971 Feld[x][y] = EL_EMC_FAKE_GRASS;
4972 DrawLevelField(x, y);
4974 else if (IS_GATE_GRAY(element) &&
4975 game.magnify_time_left > 0)
4977 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4978 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4979 IS_EM_GATE_GRAY(element) ?
4980 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4981 IS_EMC_GATE_GRAY(element) ?
4982 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4984 DrawLevelField(x, y);
4986 else if (IS_GATE_GRAY_ACTIVE(element) &&
4987 game.magnify_time_left == 0)
4989 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4990 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4991 IS_EM_GATE_GRAY_ACTIVE(element) ?
4992 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4993 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4994 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4996 DrawLevelField(x, y);
5001 static void ToggleLightSwitch(int x, int y)
5003 int element = Feld[x][y];
5005 game.light_time_left =
5006 (element == EL_LIGHT_SWITCH ?
5007 level.time_light * FRAMES_PER_SECOND : 0);
5009 RedrawAllLightSwitchesAndInvisibleElements();
5012 static void ActivateTimegateSwitch(int x, int y)
5016 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5019 SCAN_PLAYFIELD(xx, yy)
5021 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5024 int element = Feld[xx][yy];
5026 if (element == EL_TIMEGATE_CLOSED ||
5027 element == EL_TIMEGATE_CLOSING)
5029 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5030 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
5034 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5036 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5037 DrawLevelField(xx, yy);
5043 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5046 void Impact(int x, int y)
5048 boolean last_line = (y == lev_fieldy - 1);
5049 boolean object_hit = FALSE;
5050 boolean impact = (last_line || object_hit);
5051 int element = Feld[x][y];
5052 int smashed = EL_STEELWALL;
5054 if (!last_line) /* check if element below was hit */
5056 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5059 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5060 MovDir[x][y + 1] != MV_DOWN ||
5061 MovPos[x][y + 1] <= TILEY / 2));
5063 /* do not smash moving elements that left the smashed field in time */
5064 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5065 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5068 #if USE_QUICKSAND_IMPACT_BUGFIX
5069 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5071 RemoveMovingField(x, y + 1);
5072 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5073 Feld[x][y + 2] = EL_ROCK;
5074 DrawLevelField(x, y + 2);
5081 smashed = MovingOrBlocked2Element(x, y + 1);
5083 impact = (last_line || object_hit);
5086 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5088 SplashAcid(x, y + 1);
5092 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5093 /* only reset graphic animation if graphic really changes after impact */
5095 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5097 ResetGfxAnimation(x, y);
5098 DrawLevelField(x, y);
5101 if (impact && CAN_EXPLODE_IMPACT(element))
5106 else if (impact && element == EL_PEARL)
5108 ResetGfxAnimation(x, y);
5110 Feld[x][y] = EL_PEARL_BREAKING;
5111 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5114 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5116 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5121 if (impact && element == EL_AMOEBA_DROP)
5123 if (object_hit && IS_PLAYER(x, y + 1))
5124 KillPlayerUnlessEnemyProtected(x, y + 1);
5125 else if (object_hit && smashed == EL_PENGUIN)
5129 Feld[x][y] = EL_AMOEBA_GROWING;
5130 Store[x][y] = EL_AMOEBA_WET;
5132 ResetRandomAnimationValue(x, y);
5137 if (object_hit) /* check which object was hit */
5139 if (CAN_PASS_MAGIC_WALL(element) &&
5140 (smashed == EL_MAGIC_WALL ||
5141 smashed == EL_BD_MAGIC_WALL))
5144 int activated_magic_wall =
5145 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5146 EL_BD_MAGIC_WALL_ACTIVE);
5148 /* activate magic wall / mill */
5150 SCAN_PLAYFIELD(xx, yy)
5152 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5154 if (Feld[xx][yy] == smashed)
5155 Feld[xx][yy] = activated_magic_wall;
5157 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5158 game.magic_wall_active = TRUE;
5160 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5161 SND_MAGIC_WALL_ACTIVATING :
5162 SND_BD_MAGIC_WALL_ACTIVATING));
5165 if (IS_PLAYER(x, y + 1))
5167 if (CAN_SMASH_PLAYER(element))
5169 KillPlayerUnlessEnemyProtected(x, y + 1);
5173 else if (smashed == EL_PENGUIN)
5175 if (CAN_SMASH_PLAYER(element))
5181 else if (element == EL_BD_DIAMOND)
5183 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5189 else if (((element == EL_SP_INFOTRON ||
5190 element == EL_SP_ZONK) &&
5191 (smashed == EL_SP_SNIKSNAK ||
5192 smashed == EL_SP_ELECTRON ||
5193 smashed == EL_SP_DISK_ORANGE)) ||
5194 (element == EL_SP_INFOTRON &&
5195 smashed == EL_SP_DISK_YELLOW))
5200 else if (CAN_SMASH_EVERYTHING(element))
5202 if (IS_CLASSIC_ENEMY(smashed) ||
5203 CAN_EXPLODE_SMASHED(smashed))
5208 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5210 if (smashed == EL_LAMP ||
5211 smashed == EL_LAMP_ACTIVE)
5216 else if (smashed == EL_NUT)
5218 Feld[x][y + 1] = EL_NUT_BREAKING;
5219 PlayLevelSound(x, y, SND_NUT_BREAKING);
5220 RaiseScoreElement(EL_NUT);
5223 else if (smashed == EL_PEARL)
5225 ResetGfxAnimation(x, y);
5227 Feld[x][y + 1] = EL_PEARL_BREAKING;
5228 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5231 else if (smashed == EL_DIAMOND)
5233 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5234 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5237 else if (IS_BELT_SWITCH(smashed))
5239 ToggleBeltSwitch(x, y + 1);
5241 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5242 smashed == EL_SWITCHGATE_SWITCH_DOWN)
5244 ToggleSwitchgateSwitch(x, y + 1);
5246 else if (smashed == EL_LIGHT_SWITCH ||
5247 smashed == EL_LIGHT_SWITCH_ACTIVE)
5249 ToggleLightSwitch(x, y + 1);
5254 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5257 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5259 CheckElementChangeBySide(x, y + 1, smashed, element,
5260 CE_SWITCHED, CH_SIDE_TOP);
5261 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5267 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5272 /* play sound of magic wall / mill */
5274 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5275 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5277 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5278 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5279 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5280 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5285 /* play sound of object that hits the ground */
5286 if (last_line || object_hit)
5287 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5290 inline static void TurnRoundExt(int x, int y)
5302 { 0, 0 }, { 0, 0 }, { 0, 0 },
5307 int left, right, back;
5311 { MV_DOWN, MV_UP, MV_RIGHT },
5312 { MV_UP, MV_DOWN, MV_LEFT },
5314 { MV_LEFT, MV_RIGHT, MV_DOWN },
5318 { MV_RIGHT, MV_LEFT, MV_UP }
5321 int element = Feld[x][y];
5322 int move_pattern = element_info[element].move_pattern;
5324 int old_move_dir = MovDir[x][y];
5325 int left_dir = turn[old_move_dir].left;
5326 int right_dir = turn[old_move_dir].right;
5327 int back_dir = turn[old_move_dir].back;
5329 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5330 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5331 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5332 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5334 int left_x = x + left_dx, left_y = y + left_dy;
5335 int right_x = x + right_dx, right_y = y + right_dy;
5336 int move_x = x + move_dx, move_y = y + move_dy;
5340 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5342 TestIfBadThingTouchesOtherBadThing(x, y);
5344 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5345 MovDir[x][y] = right_dir;
5346 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5347 MovDir[x][y] = left_dir;
5349 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5351 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5354 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5356 TestIfBadThingTouchesOtherBadThing(x, y);
5358 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5359 MovDir[x][y] = left_dir;
5360 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5361 MovDir[x][y] = right_dir;
5363 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5365 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5368 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5370 TestIfBadThingTouchesOtherBadThing(x, y);
5372 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5373 MovDir[x][y] = left_dir;
5374 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5375 MovDir[x][y] = right_dir;
5377 if (MovDir[x][y] != old_move_dir)
5380 else if (element == EL_YAMYAM)
5382 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5383 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5385 if (can_turn_left && can_turn_right)
5386 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5387 else if (can_turn_left)
5388 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5389 else if (can_turn_right)
5390 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5392 MovDir[x][y] = back_dir;
5394 MovDelay[x][y] = 16 + 16 * RND(3);
5396 else if (element == EL_DARK_YAMYAM)
5398 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5400 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5403 if (can_turn_left && can_turn_right)
5404 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5405 else if (can_turn_left)
5406 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5407 else if (can_turn_right)
5408 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5410 MovDir[x][y] = back_dir;
5412 MovDelay[x][y] = 16 + 16 * RND(3);
5414 else if (element == EL_PACMAN)
5416 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5417 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5419 if (can_turn_left && can_turn_right)
5420 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5421 else if (can_turn_left)
5422 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5423 else if (can_turn_right)
5424 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5426 MovDir[x][y] = back_dir;
5428 MovDelay[x][y] = 6 + RND(40);
5430 else if (element == EL_PIG)
5432 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5433 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5434 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5435 boolean should_turn_left, should_turn_right, should_move_on;
5437 int rnd = RND(rnd_value);
5439 should_turn_left = (can_turn_left &&
5441 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5442 y + back_dy + left_dy)));
5443 should_turn_right = (can_turn_right &&
5445 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5446 y + back_dy + right_dy)));
5447 should_move_on = (can_move_on &&
5450 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5451 y + move_dy + left_dy) ||
5452 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5453 y + move_dy + right_dy)));
5455 if (should_turn_left || should_turn_right || should_move_on)
5457 if (should_turn_left && should_turn_right && should_move_on)
5458 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5459 rnd < 2 * rnd_value / 3 ? right_dir :
5461 else if (should_turn_left && should_turn_right)
5462 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5463 else if (should_turn_left && should_move_on)
5464 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5465 else if (should_turn_right && should_move_on)
5466 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5467 else if (should_turn_left)
5468 MovDir[x][y] = left_dir;
5469 else if (should_turn_right)
5470 MovDir[x][y] = right_dir;
5471 else if (should_move_on)
5472 MovDir[x][y] = old_move_dir;
5474 else if (can_move_on && rnd > rnd_value / 8)
5475 MovDir[x][y] = old_move_dir;
5476 else if (can_turn_left && can_turn_right)
5477 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5478 else if (can_turn_left && rnd > rnd_value / 8)
5479 MovDir[x][y] = left_dir;
5480 else if (can_turn_right && rnd > rnd_value/8)
5481 MovDir[x][y] = right_dir;
5483 MovDir[x][y] = back_dir;
5485 xx = x + move_xy[MovDir[x][y]].dx;
5486 yy = y + move_xy[MovDir[x][y]].dy;
5488 if (!IN_LEV_FIELD(xx, yy) ||
5489 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5490 MovDir[x][y] = old_move_dir;
5494 else if (element == EL_DRAGON)
5496 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5497 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5498 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5500 int rnd = RND(rnd_value);
5502 if (can_move_on && rnd > rnd_value / 8)
5503 MovDir[x][y] = old_move_dir;
5504 else if (can_turn_left && can_turn_right)
5505 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5506 else if (can_turn_left && rnd > rnd_value / 8)
5507 MovDir[x][y] = left_dir;
5508 else if (can_turn_right && rnd > rnd_value / 8)
5509 MovDir[x][y] = right_dir;
5511 MovDir[x][y] = back_dir;
5513 xx = x + move_xy[MovDir[x][y]].dx;
5514 yy = y + move_xy[MovDir[x][y]].dy;
5516 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5517 MovDir[x][y] = old_move_dir;
5521 else if (element == EL_MOLE)
5523 boolean can_move_on =
5524 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5525 IS_AMOEBOID(Feld[move_x][move_y]) ||
5526 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5529 boolean can_turn_left =
5530 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5531 IS_AMOEBOID(Feld[left_x][left_y])));
5533 boolean can_turn_right =
5534 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5535 IS_AMOEBOID(Feld[right_x][right_y])));
5537 if (can_turn_left && can_turn_right)
5538 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5539 else if (can_turn_left)
5540 MovDir[x][y] = left_dir;
5542 MovDir[x][y] = right_dir;
5545 if (MovDir[x][y] != old_move_dir)
5548 else if (element == EL_BALLOON)
5550 MovDir[x][y] = game.wind_direction;
5553 else if (element == EL_SPRING)
5555 #if USE_NEW_SPRING_BUMPER
5556 if (MovDir[x][y] & MV_HORIZONTAL)
5558 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5559 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5561 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5562 ResetGfxAnimation(move_x, move_y);
5563 DrawLevelField(move_x, move_y);
5565 MovDir[x][y] = back_dir;
5567 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5568 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5569 MovDir[x][y] = MV_NONE;
5572 if (MovDir[x][y] & MV_HORIZONTAL &&
5573 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5574 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5575 MovDir[x][y] = MV_NONE;
5580 else if (element == EL_ROBOT ||
5581 element == EL_SATELLITE ||
5582 element == EL_PENGUIN ||
5583 element == EL_EMC_ANDROID)
5585 int attr_x = -1, attr_y = -1;
5596 for (i = 0; i < MAX_PLAYERS; i++)
5598 struct PlayerInfo *player = &stored_player[i];
5599 int jx = player->jx, jy = player->jy;
5601 if (!player->active)
5605 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5613 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5614 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5615 game.engine_version < VERSION_IDENT(3,1,0,0)))
5621 if (element == EL_PENGUIN)
5624 static int xy[4][2] =
5632 for (i = 0; i < NUM_DIRECTIONS; i++)
5634 int ex = x + xy[i][0];
5635 int ey = y + xy[i][1];
5637 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5646 MovDir[x][y] = MV_NONE;
5648 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5649 else if (attr_x > x)
5650 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5652 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5653 else if (attr_y > y)
5654 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5656 if (element == EL_ROBOT)
5660 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5661 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5662 Moving2Blocked(x, y, &newx, &newy);
5664 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5665 MovDelay[x][y] = 8 + 8 * !RND(3);
5667 MovDelay[x][y] = 16;
5669 else if (element == EL_PENGUIN)
5675 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5677 boolean first_horiz = RND(2);
5678 int new_move_dir = MovDir[x][y];
5681 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5682 Moving2Blocked(x, y, &newx, &newy);
5684 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5688 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5689 Moving2Blocked(x, y, &newx, &newy);
5691 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5694 MovDir[x][y] = old_move_dir;
5698 else if (element == EL_SATELLITE)
5704 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5706 boolean first_horiz = RND(2);
5707 int new_move_dir = MovDir[x][y];
5710 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5711 Moving2Blocked(x, y, &newx, &newy);
5713 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5717 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5718 Moving2Blocked(x, y, &newx, &newy);
5720 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5723 MovDir[x][y] = old_move_dir;
5727 else if (element == EL_EMC_ANDROID)
5729 static int check_pos[16] =
5731 -1, /* 0 => (invalid) */
5732 7, /* 1 => MV_LEFT */
5733 3, /* 2 => MV_RIGHT */
5734 -1, /* 3 => (invalid) */
5736 0, /* 5 => MV_LEFT | MV_UP */
5737 2, /* 6 => MV_RIGHT | MV_UP */
5738 -1, /* 7 => (invalid) */
5739 5, /* 8 => MV_DOWN */
5740 6, /* 9 => MV_LEFT | MV_DOWN */
5741 4, /* 10 => MV_RIGHT | MV_DOWN */
5742 -1, /* 11 => (invalid) */
5743 -1, /* 12 => (invalid) */
5744 -1, /* 13 => (invalid) */
5745 -1, /* 14 => (invalid) */
5746 -1, /* 15 => (invalid) */
5754 { -1, -1, MV_LEFT | MV_UP },
5756 { +1, -1, MV_RIGHT | MV_UP },
5757 { +1, 0, MV_RIGHT },
5758 { +1, +1, MV_RIGHT | MV_DOWN },
5760 { -1, +1, MV_LEFT | MV_DOWN },
5763 int start_pos, check_order;
5764 boolean can_clone = FALSE;
5767 /* check if there is any free field around current position */
5768 for (i = 0; i < 8; i++)
5770 int newx = x + check_xy[i].dx;
5771 int newy = y + check_xy[i].dy;
5773 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5781 if (can_clone) /* randomly find an element to clone */
5785 start_pos = check_pos[RND(8)];
5786 check_order = (RND(2) ? -1 : +1);
5788 for (i = 0; i < 8; i++)
5790 int pos_raw = start_pos + i * check_order;
5791 int pos = (pos_raw + 8) % 8;
5792 int newx = x + check_xy[pos].dx;
5793 int newy = y + check_xy[pos].dy;
5795 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5797 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5798 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5800 Store[x][y] = Feld[newx][newy];
5809 if (can_clone) /* randomly find a direction to move */
5813 start_pos = check_pos[RND(8)];
5814 check_order = (RND(2) ? -1 : +1);
5816 for (i = 0; i < 8; i++)
5818 int pos_raw = start_pos + i * check_order;
5819 int pos = (pos_raw + 8) % 8;
5820 int newx = x + check_xy[pos].dx;
5821 int newy = y + check_xy[pos].dy;
5822 int new_move_dir = check_xy[pos].dir;
5824 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5826 MovDir[x][y] = new_move_dir;
5827 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5836 if (can_clone) /* cloning and moving successful */
5839 /* cannot clone -- try to move towards player */
5841 start_pos = check_pos[MovDir[x][y] & 0x0f];
5842 check_order = (RND(2) ? -1 : +1);
5844 for (i = 0; i < 3; i++)
5846 /* first check start_pos, then previous/next or (next/previous) pos */
5847 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5848 int pos = (pos_raw + 8) % 8;
5849 int newx = x + check_xy[pos].dx;
5850 int newy = y + check_xy[pos].dy;
5851 int new_move_dir = check_xy[pos].dir;
5853 if (IS_PLAYER(newx, newy))
5856 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5858 MovDir[x][y] = new_move_dir;
5859 MovDelay[x][y] = level.android_move_time * 8 + 1;
5866 else if (move_pattern == MV_TURNING_LEFT ||
5867 move_pattern == MV_TURNING_RIGHT ||
5868 move_pattern == MV_TURNING_LEFT_RIGHT ||
5869 move_pattern == MV_TURNING_RIGHT_LEFT ||
5870 move_pattern == MV_TURNING_RANDOM ||
5871 move_pattern == MV_ALL_DIRECTIONS)
5873 boolean can_turn_left =
5874 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5875 boolean can_turn_right =
5876 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5878 if (element_info[element].move_stepsize == 0) /* "not moving" */
5881 if (move_pattern == MV_TURNING_LEFT)
5882 MovDir[x][y] = left_dir;
5883 else if (move_pattern == MV_TURNING_RIGHT)
5884 MovDir[x][y] = right_dir;
5885 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5886 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5887 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5888 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5889 else if (move_pattern == MV_TURNING_RANDOM)
5890 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5891 can_turn_right && !can_turn_left ? right_dir :
5892 RND(2) ? left_dir : right_dir);
5893 else if (can_turn_left && can_turn_right)
5894 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5895 else if (can_turn_left)
5896 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5897 else if (can_turn_right)
5898 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5900 MovDir[x][y] = back_dir;
5902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5904 else if (move_pattern == MV_HORIZONTAL ||
5905 move_pattern == MV_VERTICAL)
5907 if (move_pattern & old_move_dir)
5908 MovDir[x][y] = back_dir;
5909 else if (move_pattern == MV_HORIZONTAL)
5910 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5911 else if (move_pattern == MV_VERTICAL)
5912 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5914 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5916 else if (move_pattern & MV_ANY_DIRECTION)
5918 MovDir[x][y] = move_pattern;
5919 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5921 else if (move_pattern & MV_WIND_DIRECTION)
5923 MovDir[x][y] = game.wind_direction;
5924 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5926 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5928 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5929 MovDir[x][y] = left_dir;
5930 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5931 MovDir[x][y] = right_dir;
5933 if (MovDir[x][y] != old_move_dir)
5934 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5936 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5938 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5939 MovDir[x][y] = right_dir;
5940 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5941 MovDir[x][y] = left_dir;
5943 if (MovDir[x][y] != old_move_dir)
5944 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5946 else if (move_pattern == MV_TOWARDS_PLAYER ||
5947 move_pattern == MV_AWAY_FROM_PLAYER)
5949 int attr_x = -1, attr_y = -1;
5951 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5962 for (i = 0; i < MAX_PLAYERS; i++)
5964 struct PlayerInfo *player = &stored_player[i];
5965 int jx = player->jx, jy = player->jy;
5967 if (!player->active)
5971 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5979 MovDir[x][y] = MV_NONE;
5981 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5982 else if (attr_x > x)
5983 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5985 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5986 else if (attr_y > y)
5987 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5989 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5991 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5993 boolean first_horiz = RND(2);
5994 int new_move_dir = MovDir[x][y];
5996 if (element_info[element].move_stepsize == 0) /* "not moving" */
5998 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5999 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6005 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6006 Moving2Blocked(x, y, &newx, &newy);
6008 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6012 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6013 Moving2Blocked(x, y, &newx, &newy);
6015 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6018 MovDir[x][y] = old_move_dir;
6021 else if (move_pattern == MV_WHEN_PUSHED ||
6022 move_pattern == MV_WHEN_DROPPED)
6024 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6025 MovDir[x][y] = MV_NONE;
6029 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6031 static int test_xy[7][2] =
6041 static int test_dir[7] =
6051 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6052 int move_preference = -1000000; /* start with very low preference */
6053 int new_move_dir = MV_NONE;
6054 int start_test = RND(4);
6057 for (i = 0; i < NUM_DIRECTIONS; i++)
6059 int move_dir = test_dir[start_test + i];
6060 int move_dir_preference;
6062 xx = x + test_xy[start_test + i][0];
6063 yy = y + test_xy[start_test + i][1];
6065 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6066 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6068 new_move_dir = move_dir;
6073 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6076 move_dir_preference = -1 * RunnerVisit[xx][yy];
6077 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6078 move_dir_preference = PlayerVisit[xx][yy];
6080 if (move_dir_preference > move_preference)
6082 /* prefer field that has not been visited for the longest time */
6083 move_preference = move_dir_preference;
6084 new_move_dir = move_dir;
6086 else if (move_dir_preference == move_preference &&
6087 move_dir == old_move_dir)
6089 /* prefer last direction when all directions are preferred equally */
6090 move_preference = move_dir_preference;
6091 new_move_dir = move_dir;
6095 MovDir[x][y] = new_move_dir;
6096 if (old_move_dir != new_move_dir)
6097 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6101 static void TurnRound(int x, int y)
6103 int direction = MovDir[x][y];
6105 int element, graphic;
6110 GfxDir[x][y] = MovDir[x][y];
6112 if (direction != MovDir[x][y])
6116 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6119 ResetGfxFrame(x, y, FALSE);
6121 element = Feld[x][y];
6122 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6124 if (graphic_info[graphic].anim_global_sync)
6125 GfxFrame[x][y] = FrameCounter;
6126 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
6127 GfxFrame[x][y] = CustomValue[x][y];
6128 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
6129 GfxFrame[x][y] = element_info[element].collect_score;
6130 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
6131 GfxFrame[x][y] = ChangeDelay[x][y];
6135 static boolean JustBeingPushed(int x, int y)
6139 for (i = 0; i < MAX_PLAYERS; i++)
6141 struct PlayerInfo *player = &stored_player[i];
6143 if (player->active && player->is_pushing && player->MovPos)
6145 int next_jx = player->jx + (player->jx - player->last_jx);
6146 int next_jy = player->jy + (player->jy - player->last_jy);
6148 if (x == next_jx && y == next_jy)
6156 void StartMoving(int x, int y)
6158 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6159 int element = Feld[x][y];
6164 if (MovDelay[x][y] == 0)
6165 GfxAction[x][y] = ACTION_DEFAULT;
6167 if (CAN_FALL(element) && y < lev_fieldy - 1)
6169 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6170 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6171 if (JustBeingPushed(x, y))
6174 if (element == EL_QUICKSAND_FULL)
6176 if (IS_FREE(x, y + 1))
6178 InitMovingField(x, y, MV_DOWN);
6179 started_moving = TRUE;
6181 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6182 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6183 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6184 Store[x][y] = EL_ROCK;
6186 Store[x][y] = EL_ROCK;
6189 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6191 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6193 if (!MovDelay[x][y])
6194 MovDelay[x][y] = TILEY + 1;
6203 Feld[x][y] = EL_QUICKSAND_EMPTY;
6204 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6205 Store[x][y + 1] = Store[x][y];
6208 PlayLevelSoundAction(x, y, ACTION_FILLING);
6211 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6212 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6214 InitMovingField(x, y, MV_DOWN);
6215 started_moving = TRUE;
6217 Feld[x][y] = EL_QUICKSAND_FILLING;
6218 Store[x][y] = element;
6220 PlayLevelSoundAction(x, y, ACTION_FILLING);
6222 else if (element == EL_MAGIC_WALL_FULL)
6224 if (IS_FREE(x, y + 1))
6226 InitMovingField(x, y, MV_DOWN);
6227 started_moving = TRUE;
6229 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6230 Store[x][y] = EL_CHANGED(Store[x][y]);
6232 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6234 if (!MovDelay[x][y])
6235 MovDelay[x][y] = TILEY/4 + 1;
6244 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6245 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6246 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6250 else if (element == EL_BD_MAGIC_WALL_FULL)
6252 if (IS_FREE(x, y + 1))
6254 InitMovingField(x, y, MV_DOWN);
6255 started_moving = TRUE;
6257 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6258 Store[x][y] = EL_CHANGED2(Store[x][y]);
6260 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6262 if (!MovDelay[x][y])
6263 MovDelay[x][y] = TILEY/4 + 1;
6272 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6273 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6274 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
6278 else if (CAN_PASS_MAGIC_WALL(element) &&
6279 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6280 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
6282 InitMovingField(x, y, MV_DOWN);
6283 started_moving = TRUE;
6286 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6287 EL_BD_MAGIC_WALL_FILLING);
6288 Store[x][y] = element;
6290 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6292 SplashAcid(x, y + 1);
6294 InitMovingField(x, y, MV_DOWN);
6295 started_moving = TRUE;
6297 Store[x][y] = EL_ACID;
6299 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6300 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6302 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6303 CAN_FALL(element) && WasJustFalling[x][y] &&
6304 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6306 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6307 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6308 (Feld[x][y + 1] == EL_BLOCKED)))
6310 /* this is needed for a special case not covered by calling "Impact()"
6311 from "ContinueMoving()": if an element moves to a tile directly below
6312 another element which was just falling on that tile (which was empty
6313 in the previous frame), the falling element above would just stop
6314 instead of smashing the element below (in previous version, the above
6315 element was just checked for "moving" instead of "falling", resulting
6316 in incorrect smashes caused by horizontal movement of the above
6317 element; also, the case of the player being the element to smash was
6318 simply not covered here... :-/ ) */
6320 CheckCollision[x][y] = 0;
6324 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6326 if (MovDir[x][y] == MV_NONE)
6328 InitMovingField(x, y, MV_DOWN);
6329 started_moving = TRUE;
6332 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6334 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6335 MovDir[x][y] = MV_DOWN;
6337 InitMovingField(x, y, MV_DOWN);
6338 started_moving = TRUE;
6340 else if (element == EL_AMOEBA_DROP)
6342 Feld[x][y] = EL_AMOEBA_GROWING;
6343 Store[x][y] = EL_AMOEBA_WET;
6345 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6346 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6347 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6348 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6350 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6351 (IS_FREE(x - 1, y + 1) ||
6352 Feld[x - 1][y + 1] == EL_ACID));
6353 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6354 (IS_FREE(x + 1, y + 1) ||
6355 Feld[x + 1][y + 1] == EL_ACID));
6356 boolean can_fall_any = (can_fall_left || can_fall_right);
6357 boolean can_fall_both = (can_fall_left && can_fall_right);
6358 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6360 #if USE_NEW_ALL_SLIPPERY
6361 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6363 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6364 can_fall_right = FALSE;
6365 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6366 can_fall_left = FALSE;
6367 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6368 can_fall_right = FALSE;
6369 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6370 can_fall_left = FALSE;
6372 can_fall_any = (can_fall_left || can_fall_right);
6373 can_fall_both = FALSE;
6376 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6378 if (slippery_type == SLIPPERY_ONLY_LEFT)
6379 can_fall_right = FALSE;
6380 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6381 can_fall_left = FALSE;
6382 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6383 can_fall_right = FALSE;
6384 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6385 can_fall_left = FALSE;
6387 can_fall_any = (can_fall_left || can_fall_right);
6388 can_fall_both = (can_fall_left && can_fall_right);
6392 #if USE_NEW_ALL_SLIPPERY
6394 #if USE_NEW_SP_SLIPPERY
6395 /* !!! better use the same properties as for custom elements here !!! */
6396 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6397 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6399 can_fall_right = FALSE; /* slip down on left side */
6400 can_fall_both = FALSE;
6405 #if USE_NEW_ALL_SLIPPERY
6408 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6409 can_fall_right = FALSE; /* slip down on left side */
6411 can_fall_left = !(can_fall_right = RND(2));
6413 can_fall_both = FALSE;
6418 if (game.emulation == EMU_BOULDERDASH ||
6419 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6420 can_fall_right = FALSE; /* slip down on left side */
6422 can_fall_left = !(can_fall_right = RND(2));
6424 can_fall_both = FALSE;
6430 /* if not determined otherwise, prefer left side for slipping down */
6431 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6432 started_moving = TRUE;
6436 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6438 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6441 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6442 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6443 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6444 int belt_dir = game.belt_dir[belt_nr];
6446 if ((belt_dir == MV_LEFT && left_is_free) ||
6447 (belt_dir == MV_RIGHT && right_is_free))
6449 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6451 InitMovingField(x, y, belt_dir);
6452 started_moving = TRUE;
6454 Pushed[x][y] = TRUE;
6455 Pushed[nextx][y] = TRUE;
6457 GfxAction[x][y] = ACTION_DEFAULT;
6461 MovDir[x][y] = 0; /* if element was moving, stop it */
6466 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6468 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6470 if (CAN_MOVE(element) && !started_moving)
6473 int move_pattern = element_info[element].move_pattern;
6478 if (MovDir[x][y] == MV_NONE)
6480 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6481 x, y, element, element_info[element].token_name);
6482 printf("StartMoving(): This should never happen!\n");
6487 Moving2Blocked(x, y, &newx, &newy);
6489 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6492 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6493 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6495 WasJustMoving[x][y] = 0;
6496 CheckCollision[x][y] = 0;
6498 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6500 if (Feld[x][y] != element) /* element has changed */
6504 if (!MovDelay[x][y]) /* start new movement phase */
6506 /* all objects that can change their move direction after each step
6507 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6509 if (element != EL_YAMYAM &&
6510 element != EL_DARK_YAMYAM &&
6511 element != EL_PACMAN &&
6512 !(move_pattern & MV_ANY_DIRECTION) &&
6513 move_pattern != MV_TURNING_LEFT &&
6514 move_pattern != MV_TURNING_RIGHT &&
6515 move_pattern != MV_TURNING_LEFT_RIGHT &&
6516 move_pattern != MV_TURNING_RIGHT_LEFT &&
6517 move_pattern != MV_TURNING_RANDOM)
6521 if (MovDelay[x][y] && (element == EL_BUG ||
6522 element == EL_SPACESHIP ||
6523 element == EL_SP_SNIKSNAK ||
6524 element == EL_SP_ELECTRON ||
6525 element == EL_MOLE))
6526 DrawLevelField(x, y);
6530 if (MovDelay[x][y]) /* wait some time before next movement */
6534 if (element == EL_ROBOT ||
6535 element == EL_YAMYAM ||
6536 element == EL_DARK_YAMYAM)
6538 DrawLevelElementAnimationIfNeeded(x, y, element);
6539 PlayLevelSoundAction(x, y, ACTION_WAITING);
6541 else if (element == EL_SP_ELECTRON)
6542 DrawLevelElementAnimationIfNeeded(x, y, element);
6543 else if (element == EL_DRAGON)
6546 int dir = MovDir[x][y];
6547 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6548 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6549 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6550 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6551 dir == MV_UP ? IMG_FLAMES_1_UP :
6552 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6553 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6555 GfxAction[x][y] = ACTION_ATTACKING;
6557 if (IS_PLAYER(x, y))
6558 DrawPlayerField(x, y);
6560 DrawLevelField(x, y);
6562 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6564 for (i = 1; i <= 3; i++)
6566 int xx = x + i * dx;
6567 int yy = y + i * dy;
6568 int sx = SCREENX(xx);
6569 int sy = SCREENY(yy);
6570 int flame_graphic = graphic + (i - 1);
6572 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6577 int flamed = MovingOrBlocked2Element(xx, yy);
6581 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6583 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6584 RemoveMovingField(xx, yy);
6586 RemoveField(xx, yy);
6588 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6591 RemoveMovingField(xx, yy);
6594 ChangeDelay[xx][yy] = 0;
6596 Feld[xx][yy] = EL_FLAMES;
6598 if (IN_SCR_FIELD(sx, sy))
6600 DrawLevelFieldCrumbledSand(xx, yy);
6601 DrawGraphic(sx, sy, flame_graphic, frame);
6606 if (Feld[xx][yy] == EL_FLAMES)
6607 Feld[xx][yy] = EL_EMPTY;
6608 DrawLevelField(xx, yy);
6613 if (MovDelay[x][y]) /* element still has to wait some time */
6615 PlayLevelSoundAction(x, y, ACTION_WAITING);
6621 /* now make next step */
6623 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6625 if (DONT_COLLIDE_WITH(element) &&
6626 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6627 !PLAYER_ENEMY_PROTECTED(newx, newy))
6629 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6634 else if (CAN_MOVE_INTO_ACID(element) &&
6635 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6636 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6637 (MovDir[x][y] == MV_DOWN ||
6638 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6640 SplashAcid(newx, newy);
6641 Store[x][y] = EL_ACID;
6643 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6645 if (Feld[newx][newy] == EL_EXIT_OPEN)
6648 DrawLevelField(x, y);
6650 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6651 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6652 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6654 local_player->friends_still_needed--;
6655 if (!local_player->friends_still_needed &&
6656 !local_player->GameOver && AllPlayersGone)
6657 local_player->LevelSolved = local_player->GameOver = TRUE;
6661 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6663 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6664 DrawLevelField(newx, newy);
6666 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6668 else if (!IS_FREE(newx, newy))
6670 GfxAction[x][y] = ACTION_WAITING;
6672 if (IS_PLAYER(x, y))
6673 DrawPlayerField(x, y);
6675 DrawLevelField(x, y);
6680 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6682 if (IS_FOOD_PIG(Feld[newx][newy]))
6684 if (IS_MOVING(newx, newy))
6685 RemoveMovingField(newx, newy);
6688 Feld[newx][newy] = EL_EMPTY;
6689 DrawLevelField(newx, newy);
6692 PlayLevelSound(x, y, SND_PIG_DIGGING);
6694 else if (!IS_FREE(newx, newy))
6696 if (IS_PLAYER(x, y))
6697 DrawPlayerField(x, y);
6699 DrawLevelField(x, y);
6704 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6706 if (Store[x][y] != EL_EMPTY)
6708 boolean can_clone = FALSE;
6711 /* check if element to clone is still there */
6712 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6714 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6722 /* cannot clone or target field not free anymore -- do not clone */
6723 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6724 Store[x][y] = EL_EMPTY;
6727 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6729 if (IS_MV_DIAGONAL(MovDir[x][y]))
6731 int diagonal_move_dir = MovDir[x][y];
6732 int stored = Store[x][y];
6733 int change_delay = 8;
6736 /* android is moving diagonally */
6738 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6740 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6741 GfxElement[x][y] = EL_EMC_ANDROID;
6742 GfxAction[x][y] = ACTION_SHRINKING;
6743 GfxDir[x][y] = diagonal_move_dir;
6744 ChangeDelay[x][y] = change_delay;
6746 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6749 DrawLevelGraphicAnimation(x, y, graphic);
6750 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6752 if (Feld[newx][newy] == EL_ACID)
6754 SplashAcid(newx, newy);
6759 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6761 Store[newx][newy] = EL_EMC_ANDROID;
6762 GfxElement[newx][newy] = EL_EMC_ANDROID;
6763 GfxAction[newx][newy] = ACTION_GROWING;
6764 GfxDir[newx][newy] = diagonal_move_dir;
6765 ChangeDelay[newx][newy] = change_delay;
6767 graphic = el_act_dir2img(GfxElement[newx][newy],
6768 GfxAction[newx][newy], GfxDir[newx][newy]);
6770 DrawLevelGraphicAnimation(newx, newy, graphic);
6771 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6777 Feld[newx][newy] = EL_EMPTY;
6778 DrawLevelField(newx, newy);
6780 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6783 else if (!IS_FREE(newx, newy))
6786 if (IS_PLAYER(x, y))
6787 DrawPlayerField(x, y);
6789 DrawLevelField(x, y);
6795 else if (IS_CUSTOM_ELEMENT(element) &&
6796 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6798 int new_element = Feld[newx][newy];
6800 if (!IS_FREE(newx, newy))
6802 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6803 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6806 /* no element can dig solid indestructible elements */
6807 if (IS_INDESTRUCTIBLE(new_element) &&
6808 !IS_DIGGABLE(new_element) &&
6809 !IS_COLLECTIBLE(new_element))
6812 if (AmoebaNr[newx][newy] &&
6813 (new_element == EL_AMOEBA_FULL ||
6814 new_element == EL_BD_AMOEBA ||
6815 new_element == EL_AMOEBA_GROWING))
6817 AmoebaCnt[AmoebaNr[newx][newy]]--;
6818 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6821 if (IS_MOVING(newx, newy))
6822 RemoveMovingField(newx, newy);
6825 RemoveField(newx, newy);
6826 DrawLevelField(newx, newy);
6829 /* if digged element was about to explode, prevent the explosion */
6830 ExplodeField[newx][newy] = EX_TYPE_NONE;
6832 PlayLevelSoundAction(x, y, action);
6835 Store[newx][newy] = EL_EMPTY;
6837 /* this makes it possible to leave the removed element again */
6838 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6839 Store[newx][newy] = new_element;
6841 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6843 int move_leave_element = element_info[element].move_leave_element;
6845 /* this makes it possible to leave the removed element again */
6846 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6847 new_element : move_leave_element);
6851 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6853 RunnerVisit[x][y] = FrameCounter;
6854 PlayerVisit[x][y] /= 8; /* expire player visit path */
6857 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6859 if (!IS_FREE(newx, newy))
6861 if (IS_PLAYER(x, y))
6862 DrawPlayerField(x, y);
6864 DrawLevelField(x, y);
6870 boolean wanna_flame = !RND(10);
6871 int dx = newx - x, dy = newy - y;
6872 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6873 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6874 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6875 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6876 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6877 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6880 IS_CLASSIC_ENEMY(element1) ||
6881 IS_CLASSIC_ENEMY(element2)) &&
6882 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6883 element1 != EL_FLAMES && element2 != EL_FLAMES)
6885 ResetGfxAnimation(x, y);
6886 GfxAction[x][y] = ACTION_ATTACKING;
6888 if (IS_PLAYER(x, y))
6889 DrawPlayerField(x, y);
6891 DrawLevelField(x, y);
6893 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6895 MovDelay[x][y] = 50;
6899 RemoveField(newx, newy);
6901 Feld[newx][newy] = EL_FLAMES;
6902 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6905 RemoveField(newx1, newy1);
6907 Feld[newx1][newy1] = EL_FLAMES;
6909 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6912 RemoveField(newx2, newy2);
6914 Feld[newx2][newy2] = EL_FLAMES;
6921 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6922 Feld[newx][newy] == EL_DIAMOND)
6924 if (IS_MOVING(newx, newy))
6925 RemoveMovingField(newx, newy);
6928 Feld[newx][newy] = EL_EMPTY;
6929 DrawLevelField(newx, newy);
6932 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6934 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6935 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6937 if (AmoebaNr[newx][newy])
6939 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6940 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6941 Feld[newx][newy] == EL_BD_AMOEBA)
6942 AmoebaCnt[AmoebaNr[newx][newy]]--;
6947 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6949 RemoveMovingField(newx, newy);
6952 if (IS_MOVING(newx, newy))
6954 RemoveMovingField(newx, newy);
6959 Feld[newx][newy] = EL_EMPTY;
6960 DrawLevelField(newx, newy);
6963 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6965 else if ((element == EL_PACMAN || element == EL_MOLE)
6966 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6968 if (AmoebaNr[newx][newy])
6970 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6971 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6972 Feld[newx][newy] == EL_BD_AMOEBA)
6973 AmoebaCnt[AmoebaNr[newx][newy]]--;
6976 if (element == EL_MOLE)
6978 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6979 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6981 ResetGfxAnimation(x, y);
6982 GfxAction[x][y] = ACTION_DIGGING;
6983 DrawLevelField(x, y);
6985 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6987 return; /* wait for shrinking amoeba */
6989 else /* element == EL_PACMAN */
6991 Feld[newx][newy] = EL_EMPTY;
6992 DrawLevelField(newx, newy);
6993 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6996 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6997 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6998 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7000 /* wait for shrinking amoeba to completely disappear */
7003 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7005 /* object was running against a wall */
7010 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7011 if (move_pattern & MV_ANY_DIRECTION &&
7012 move_pattern == MovDir[x][y])
7014 int blocking_element =
7015 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7017 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7020 element = Feld[x][y]; /* element might have changed */
7024 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7025 DrawLevelElementAnimation(x, y, element);
7027 if (DONT_TOUCH(element))
7028 TestIfBadThingTouchesPlayer(x, y);
7033 InitMovingField(x, y, MovDir[x][y]);
7035 PlayLevelSoundAction(x, y, ACTION_MOVING);
7039 ContinueMoving(x, y);
7042 void ContinueMoving(int x, int y)
7044 int element = Feld[x][y];
7045 struct ElementInfo *ei = &element_info[element];
7046 int direction = MovDir[x][y];
7047 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7048 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7049 int newx = x + dx, newy = y + dy;
7050 int stored = Store[x][y];
7051 int stored_new = Store[newx][newy];
7052 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7053 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7054 boolean last_line = (newy == lev_fieldy - 1);
7056 MovPos[x][y] += getElementMoveStepsize(x, y);
7058 if (pushed_by_player) /* special case: moving object pushed by player */
7059 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7061 if (ABS(MovPos[x][y]) < TILEX)
7063 DrawLevelField(x, y);
7065 return; /* element is still moving */
7068 /* element reached destination field */
7070 Feld[x][y] = EL_EMPTY;
7071 Feld[newx][newy] = element;
7072 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7074 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7076 element = Feld[newx][newy] = EL_ACID;
7078 else if (element == EL_MOLE)
7080 Feld[x][y] = EL_SAND;
7082 DrawLevelFieldCrumbledSandNeighbours(x, y);
7084 else if (element == EL_QUICKSAND_FILLING)
7086 element = Feld[newx][newy] = get_next_element(element);
7087 Store[newx][newy] = Store[x][y];
7089 else if (element == EL_QUICKSAND_EMPTYING)
7091 Feld[x][y] = get_next_element(element);
7092 element = Feld[newx][newy] = Store[x][y];
7094 else if (element == EL_MAGIC_WALL_FILLING)
7096 element = Feld[newx][newy] = get_next_element(element);
7097 if (!game.magic_wall_active)
7098 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7099 Store[newx][newy] = Store[x][y];
7101 else if (element == EL_MAGIC_WALL_EMPTYING)
7103 Feld[x][y] = get_next_element(element);
7104 if (!game.magic_wall_active)
7105 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7106 element = Feld[newx][newy] = Store[x][y];
7108 #if USE_NEW_CUSTOM_VALUE
7109 InitField(newx, newy, FALSE);
7112 else if (element == EL_BD_MAGIC_WALL_FILLING)
7114 element = Feld[newx][newy] = get_next_element(element);
7115 if (!game.magic_wall_active)
7116 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7117 Store[newx][newy] = Store[x][y];
7119 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7121 Feld[x][y] = get_next_element(element);
7122 if (!game.magic_wall_active)
7123 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7124 element = Feld[newx][newy] = Store[x][y];
7126 #if USE_NEW_CUSTOM_VALUE
7127 InitField(newx, newy, FALSE);
7130 else if (element == EL_AMOEBA_DROPPING)
7132 Feld[x][y] = get_next_element(element);
7133 element = Feld[newx][newy] = Store[x][y];
7135 else if (element == EL_SOKOBAN_OBJECT)
7138 Feld[x][y] = Back[x][y];
7140 if (Back[newx][newy])
7141 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7143 Back[x][y] = Back[newx][newy] = 0;
7146 Store[x][y] = EL_EMPTY;
7151 MovDelay[newx][newy] = 0;
7154 if (CAN_CHANGE_OR_HAS_ACTION(element))
7156 if (CAN_CHANGE(element))
7159 /* copy element change control values to new field */
7160 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7161 ChangePage[newx][newy] = ChangePage[x][y];
7162 ChangeCount[newx][newy] = ChangeCount[x][y];
7163 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7166 #if USE_NEW_CUSTOM_VALUE
7167 CustomValue[newx][newy] = CustomValue[x][y];
7173 #if USE_NEW_CUSTOM_VALUE
7174 CustomValue[newx][newy] = CustomValue[x][y];
7178 ChangeDelay[x][y] = 0;
7179 ChangePage[x][y] = -1;
7180 ChangeCount[x][y] = 0;
7181 ChangeEvent[x][y] = -1;
7183 #if USE_NEW_CUSTOM_VALUE
7184 CustomValue[x][y] = 0;
7187 /* copy animation control values to new field */
7188 GfxFrame[newx][newy] = GfxFrame[x][y];
7189 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7190 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7191 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7193 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7195 /* some elements can leave other elements behind after moving */
7197 if (ei->move_leave_element != EL_EMPTY &&
7198 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7199 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7201 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7202 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7203 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7206 int move_leave_element = ei->move_leave_element;
7210 /* this makes it possible to leave the removed element again */
7211 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7212 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7214 /* this makes it possible to leave the removed element again */
7215 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7216 move_leave_element = stored;
7219 /* this makes it possible to leave the removed element again */
7220 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7221 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7222 move_leave_element = stored;
7225 Feld[x][y] = move_leave_element;
7227 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7228 MovDir[x][y] = direction;
7230 InitField(x, y, FALSE);
7232 if (GFX_CRUMBLED(Feld[x][y]))
7233 DrawLevelFieldCrumbledSandNeighbours(x, y);
7235 if (ELEM_IS_PLAYER(move_leave_element))
7236 RelocatePlayer(x, y, move_leave_element);
7239 /* do this after checking for left-behind element */
7240 ResetGfxAnimation(x, y); /* reset animation values for old field */
7242 if (!CAN_MOVE(element) ||
7243 (CAN_FALL(element) && direction == MV_DOWN &&
7244 (element == EL_SPRING ||
7245 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7246 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7247 GfxDir[x][y] = MovDir[newx][newy] = 0;
7249 DrawLevelField(x, y);
7250 DrawLevelField(newx, newy);
7252 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7254 /* prevent pushed element from moving on in pushed direction */
7255 if (pushed_by_player && CAN_MOVE(element) &&
7256 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7257 !(element_info[element].move_pattern & direction))
7258 TurnRound(newx, newy);
7260 /* prevent elements on conveyor belt from moving on in last direction */
7261 if (pushed_by_conveyor && CAN_FALL(element) &&
7262 direction & MV_HORIZONTAL)
7263 MovDir[newx][newy] = 0;
7265 if (!pushed_by_player)
7267 int nextx = newx + dx, nexty = newy + dy;
7268 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7270 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7272 if (CAN_FALL(element) && direction == MV_DOWN)
7273 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7275 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7276 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7279 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7281 TestIfBadThingTouchesPlayer(newx, newy);
7282 TestIfBadThingTouchesFriend(newx, newy);
7284 if (!IS_CUSTOM_ELEMENT(element))
7285 TestIfBadThingTouchesOtherBadThing(newx, newy);
7287 else if (element == EL_PENGUIN)
7288 TestIfFriendTouchesBadThing(newx, newy);
7290 /* give the player one last chance (one more frame) to move away */
7291 if (CAN_FALL(element) && direction == MV_DOWN &&
7292 (last_line || (!IS_FREE(x, newy + 1) &&
7293 (!IS_PLAYER(x, newy + 1) ||
7294 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7297 if (pushed_by_player && !game.use_change_when_pushing_bug)
7299 int push_side = MV_DIR_OPPOSITE(direction);
7300 struct PlayerInfo *player = PLAYERINFO(x, y);
7302 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7303 player->index_bit, push_side);
7304 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7305 player->index_bit, push_side);
7308 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7309 MovDelay[newx][newy] = 1;
7311 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7313 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7316 if (ChangePage[newx][newy] != -1) /* delayed change */
7318 int page = ChangePage[newx][newy];
7319 struct ElementChangeInfo *change = &ei->change_page[page];
7321 ChangePage[newx][newy] = -1;
7323 if (change->can_change)
7325 if (ChangeElement(newx, newy, element, page))
7327 if (change->post_change_function)
7328 change->post_change_function(newx, newy);
7332 if (change->has_action)
7333 ExecuteCustomElementAction(newx, newy, element, page);
7337 TestIfElementHitsCustomElement(newx, newy, direction);
7338 TestIfPlayerTouchesCustomElement(newx, newy);
7339 TestIfElementTouchesCustomElement(newx, newy);
7342 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7343 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7344 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7345 MV_DIR_OPPOSITE(direction));
7349 int AmoebeNachbarNr(int ax, int ay)
7352 int element = Feld[ax][ay];
7354 static int xy[4][2] =
7362 for (i = 0; i < NUM_DIRECTIONS; i++)
7364 int x = ax + xy[i][0];
7365 int y = ay + xy[i][1];
7367 if (!IN_LEV_FIELD(x, y))
7370 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7371 group_nr = AmoebaNr[x][y];
7377 void AmoebenVereinigen(int ax, int ay)
7379 int i, x, y, xx, yy;
7380 int new_group_nr = AmoebaNr[ax][ay];
7381 static int xy[4][2] =
7389 if (new_group_nr == 0)
7392 for (i = 0; i < NUM_DIRECTIONS; i++)
7397 if (!IN_LEV_FIELD(x, y))
7400 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7401 Feld[x][y] == EL_BD_AMOEBA ||
7402 Feld[x][y] == EL_AMOEBA_DEAD) &&
7403 AmoebaNr[x][y] != new_group_nr)
7405 int old_group_nr = AmoebaNr[x][y];
7407 if (old_group_nr == 0)
7410 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7411 AmoebaCnt[old_group_nr] = 0;
7412 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7413 AmoebaCnt2[old_group_nr] = 0;
7416 SCAN_PLAYFIELD(xx, yy)
7418 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7421 if (AmoebaNr[xx][yy] == old_group_nr)
7422 AmoebaNr[xx][yy] = new_group_nr;
7428 void AmoebeUmwandeln(int ax, int ay)
7432 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7434 int group_nr = AmoebaNr[ax][ay];
7439 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7440 printf("AmoebeUmwandeln(): This should never happen!\n");
7446 SCAN_PLAYFIELD(x, y)
7448 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7451 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7454 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7458 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7459 SND_AMOEBA_TURNING_TO_GEM :
7460 SND_AMOEBA_TURNING_TO_ROCK));
7465 static int xy[4][2] =
7473 for (i = 0; i < NUM_DIRECTIONS; i++)
7478 if (!IN_LEV_FIELD(x, y))
7481 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7483 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7484 SND_AMOEBA_TURNING_TO_GEM :
7485 SND_AMOEBA_TURNING_TO_ROCK));
7492 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7495 int group_nr = AmoebaNr[ax][ay];
7496 boolean done = FALSE;
7501 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7502 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7508 SCAN_PLAYFIELD(x, y)
7510 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7513 if (AmoebaNr[x][y] == group_nr &&
7514 (Feld[x][y] == EL_AMOEBA_DEAD ||
7515 Feld[x][y] == EL_BD_AMOEBA ||
7516 Feld[x][y] == EL_AMOEBA_GROWING))
7519 Feld[x][y] = new_element;
7520 InitField(x, y, FALSE);
7521 DrawLevelField(x, y);
7527 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7528 SND_BD_AMOEBA_TURNING_TO_ROCK :
7529 SND_BD_AMOEBA_TURNING_TO_GEM));
7532 void AmoebeWaechst(int x, int y)
7534 static unsigned long sound_delay = 0;
7535 static unsigned long sound_delay_value = 0;
7537 if (!MovDelay[x][y]) /* start new growing cycle */
7541 if (DelayReached(&sound_delay, sound_delay_value))
7543 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7544 sound_delay_value = 30;
7548 if (MovDelay[x][y]) /* wait some time before growing bigger */
7551 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7553 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7554 6 - MovDelay[x][y]);
7556 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7559 if (!MovDelay[x][y])
7561 Feld[x][y] = Store[x][y];
7563 DrawLevelField(x, y);
7568 void AmoebaDisappearing(int x, int y)
7570 static unsigned long sound_delay = 0;
7571 static unsigned long sound_delay_value = 0;
7573 if (!MovDelay[x][y]) /* start new shrinking cycle */
7577 if (DelayReached(&sound_delay, sound_delay_value))
7578 sound_delay_value = 30;
7581 if (MovDelay[x][y]) /* wait some time before shrinking */
7584 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7586 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7587 6 - MovDelay[x][y]);
7589 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7592 if (!MovDelay[x][y])
7594 Feld[x][y] = EL_EMPTY;
7595 DrawLevelField(x, y);
7597 /* don't let mole enter this field in this cycle;
7598 (give priority to objects falling to this field from above) */
7604 void AmoebeAbleger(int ax, int ay)
7607 int element = Feld[ax][ay];
7608 int graphic = el2img(element);
7609 int newax = ax, neway = ay;
7610 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7611 static int xy[4][2] =
7619 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7621 Feld[ax][ay] = EL_AMOEBA_DEAD;
7622 DrawLevelField(ax, ay);
7626 if (IS_ANIMATED(graphic))
7627 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7629 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7630 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7632 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7635 if (MovDelay[ax][ay])
7639 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7642 int x = ax + xy[start][0];
7643 int y = ay + xy[start][1];
7645 if (!IN_LEV_FIELD(x, y))
7648 if (IS_FREE(x, y) ||
7649 CAN_GROW_INTO(Feld[x][y]) ||
7650 Feld[x][y] == EL_QUICKSAND_EMPTY)
7656 if (newax == ax && neway == ay)
7659 else /* normal or "filled" (BD style) amoeba */
7662 boolean waiting_for_player = FALSE;
7664 for (i = 0; i < NUM_DIRECTIONS; i++)
7666 int j = (start + i) % 4;
7667 int x = ax + xy[j][0];
7668 int y = ay + xy[j][1];
7670 if (!IN_LEV_FIELD(x, y))
7673 if (IS_FREE(x, y) ||
7674 CAN_GROW_INTO(Feld[x][y]) ||
7675 Feld[x][y] == EL_QUICKSAND_EMPTY)
7681 else if (IS_PLAYER(x, y))
7682 waiting_for_player = TRUE;
7685 if (newax == ax && neway == ay) /* amoeba cannot grow */
7687 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7689 Feld[ax][ay] = EL_AMOEBA_DEAD;
7690 DrawLevelField(ax, ay);
7691 AmoebaCnt[AmoebaNr[ax][ay]]--;
7693 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7695 if (element == EL_AMOEBA_FULL)
7696 AmoebeUmwandeln(ax, ay);
7697 else if (element == EL_BD_AMOEBA)
7698 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7703 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7705 /* amoeba gets larger by growing in some direction */
7707 int new_group_nr = AmoebaNr[ax][ay];
7710 if (new_group_nr == 0)
7712 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7713 printf("AmoebeAbleger(): This should never happen!\n");
7718 AmoebaNr[newax][neway] = new_group_nr;
7719 AmoebaCnt[new_group_nr]++;
7720 AmoebaCnt2[new_group_nr]++;
7722 /* if amoeba touches other amoeba(s) after growing, unify them */
7723 AmoebenVereinigen(newax, neway);
7725 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7727 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7733 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7734 (neway == lev_fieldy - 1 && newax != ax))
7736 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7737 Store[newax][neway] = element;
7739 else if (neway == ay || element == EL_EMC_DRIPPER)
7741 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7743 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7747 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7748 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7749 Store[ax][ay] = EL_AMOEBA_DROP;
7750 ContinueMoving(ax, ay);
7754 DrawLevelField(newax, neway);
7757 void Life(int ax, int ay)
7761 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7764 int element = Feld[ax][ay];
7765 int graphic = el2img(element);
7766 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7768 boolean changed = FALSE;
7770 if (IS_ANIMATED(graphic))
7771 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7776 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7777 MovDelay[ax][ay] = life_time;
7779 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7782 if (MovDelay[ax][ay])
7786 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7788 int xx = ax+x1, yy = ay+y1;
7791 if (!IN_LEV_FIELD(xx, yy))
7794 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7796 int x = xx+x2, y = yy+y2;
7798 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7801 if (((Feld[x][y] == element ||
7802 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7804 (IS_FREE(x, y) && Stop[x][y]))
7808 if (xx == ax && yy == ay) /* field in the middle */
7810 if (nachbarn < life_parameter[0] ||
7811 nachbarn > life_parameter[1])
7813 Feld[xx][yy] = EL_EMPTY;
7815 DrawLevelField(xx, yy);
7816 Stop[xx][yy] = TRUE;
7820 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7821 { /* free border field */
7822 if (nachbarn >= life_parameter[2] &&
7823 nachbarn <= life_parameter[3])
7825 Feld[xx][yy] = element;
7826 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7828 DrawLevelField(xx, yy);
7829 Stop[xx][yy] = TRUE;
7836 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7837 SND_GAME_OF_LIFE_GROWING);
7840 static void InitRobotWheel(int x, int y)
7842 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7845 static void RunRobotWheel(int x, int y)
7847 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7850 static void StopRobotWheel(int x, int y)
7852 if (ZX == x && ZY == y)
7856 static void InitTimegateWheel(int x, int y)
7858 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7861 static void RunTimegateWheel(int x, int y)
7863 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7866 static void InitMagicBallDelay(int x, int y)
7869 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7871 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7875 static void ActivateMagicBall(int bx, int by)
7879 if (level.ball_random)
7881 int pos_border = RND(8); /* select one of the eight border elements */
7882 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7883 int xx = pos_content % 3;
7884 int yy = pos_content / 3;
7889 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7890 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7894 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7896 int xx = x - bx + 1;
7897 int yy = y - by + 1;
7899 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7900 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7904 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7907 static void InitDiagonalMovingElement(int x, int y)
7910 MovDelay[x][y] = level.android_move_time;
7914 void CheckExit(int x, int y)
7916 if (local_player->gems_still_needed > 0 ||
7917 local_player->sokobanfields_still_needed > 0 ||
7918 local_player->lights_still_needed > 0)
7920 int element = Feld[x][y];
7921 int graphic = el2img(element);
7923 if (IS_ANIMATED(graphic))
7924 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7929 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7932 Feld[x][y] = EL_EXIT_OPENING;
7934 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7937 void CheckExitSP(int x, int y)
7939 if (local_player->gems_still_needed > 0)
7941 int element = Feld[x][y];
7942 int graphic = el2img(element);
7944 if (IS_ANIMATED(graphic))
7945 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7950 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7953 Feld[x][y] = EL_SP_EXIT_OPENING;
7955 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7958 static void CloseAllOpenTimegates()
7963 SCAN_PLAYFIELD(x, y)
7965 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7968 int element = Feld[x][y];
7970 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7972 Feld[x][y] = EL_TIMEGATE_CLOSING;
7974 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7979 void EdelsteinFunkeln(int x, int y)
7981 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7984 if (Feld[x][y] == EL_BD_DIAMOND)
7987 if (MovDelay[x][y] == 0) /* next animation frame */
7988 MovDelay[x][y] = 11 * !SimpleRND(500);
7990 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7994 if (setup.direct_draw && MovDelay[x][y])
7995 SetDrawtoField(DRAW_BUFFERED);
7997 DrawLevelElementAnimation(x, y, Feld[x][y]);
7999 if (MovDelay[x][y] != 0)
8001 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8002 10 - MovDelay[x][y]);
8004 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8006 if (setup.direct_draw)
8010 dest_x = FX + SCREENX(x) * TILEX;
8011 dest_y = FY + SCREENY(y) * TILEY;
8013 BlitBitmap(drawto_field, window,
8014 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8015 SetDrawtoField(DRAW_DIRECT);
8021 void MauerWaechst(int x, int y)
8025 if (!MovDelay[x][y]) /* next animation frame */
8026 MovDelay[x][y] = 3 * delay;
8028 if (MovDelay[x][y]) /* wait some time before next frame */
8032 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8034 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8035 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8037 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8040 if (!MovDelay[x][y])
8042 if (MovDir[x][y] == MV_LEFT)
8044 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8045 DrawLevelField(x - 1, y);
8047 else if (MovDir[x][y] == MV_RIGHT)
8049 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8050 DrawLevelField(x + 1, y);
8052 else if (MovDir[x][y] == MV_UP)
8054 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8055 DrawLevelField(x, y - 1);
8059 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8060 DrawLevelField(x, y + 1);
8063 Feld[x][y] = Store[x][y];
8065 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8066 DrawLevelField(x, y);
8071 void MauerAbleger(int ax, int ay)
8073 int element = Feld[ax][ay];
8074 int graphic = el2img(element);
8075 boolean oben_frei = FALSE, unten_frei = FALSE;
8076 boolean links_frei = FALSE, rechts_frei = FALSE;
8077 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8078 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8079 boolean new_wall = FALSE;
8081 if (IS_ANIMATED(graphic))
8082 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8084 if (!MovDelay[ax][ay]) /* start building new wall */
8085 MovDelay[ax][ay] = 6;
8087 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8090 if (MovDelay[ax][ay])
8094 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8096 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8098 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8100 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8103 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8104 element == EL_EXPANDABLE_WALL_ANY)
8108 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8109 Store[ax][ay-1] = element;
8110 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8111 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8112 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8113 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8118 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8119 Store[ax][ay+1] = element;
8120 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8121 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8122 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8123 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8128 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8129 element == EL_EXPANDABLE_WALL_ANY ||
8130 element == EL_EXPANDABLE_WALL ||
8131 element == EL_BD_EXPANDABLE_WALL)
8135 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8136 Store[ax-1][ay] = element;
8137 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8138 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8139 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8140 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8146 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8147 Store[ax+1][ay] = element;
8148 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8149 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8150 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8151 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8156 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8157 DrawLevelField(ax, ay);
8159 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8161 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8162 unten_massiv = TRUE;
8163 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8164 links_massiv = TRUE;
8165 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8166 rechts_massiv = TRUE;
8168 if (((oben_massiv && unten_massiv) ||
8169 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8170 element == EL_EXPANDABLE_WALL) &&
8171 ((links_massiv && rechts_massiv) ||
8172 element == EL_EXPANDABLE_WALL_VERTICAL))
8173 Feld[ax][ay] = EL_WALL;
8176 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8179 void CheckForDragon(int x, int y)
8182 boolean dragon_found = FALSE;
8183 static int xy[4][2] =
8191 for (i = 0; i < NUM_DIRECTIONS; i++)
8193 for (j = 0; j < 4; j++)
8195 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8197 if (IN_LEV_FIELD(xx, yy) &&
8198 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8200 if (Feld[xx][yy] == EL_DRAGON)
8201 dragon_found = TRUE;
8210 for (i = 0; i < NUM_DIRECTIONS; i++)
8212 for (j = 0; j < 3; j++)
8214 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8216 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8218 Feld[xx][yy] = EL_EMPTY;
8219 DrawLevelField(xx, yy);
8228 static void InitBuggyBase(int x, int y)
8230 int element = Feld[x][y];
8231 int activating_delay = FRAMES_PER_SECOND / 4;
8234 (element == EL_SP_BUGGY_BASE ?
8235 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8236 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8238 element == EL_SP_BUGGY_BASE_ACTIVE ?
8239 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8242 static void WarnBuggyBase(int x, int y)
8245 static int xy[4][2] =
8253 for (i = 0; i < NUM_DIRECTIONS; i++)
8255 int xx = x + xy[i][0];
8256 int yy = y + xy[i][1];
8258 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8260 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8267 static void InitTrap(int x, int y)
8269 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8272 static void ActivateTrap(int x, int y)
8274 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8277 static void ChangeActiveTrap(int x, int y)
8279 int graphic = IMG_TRAP_ACTIVE;
8281 /* if new animation frame was drawn, correct crumbled sand border */
8282 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8283 DrawLevelFieldCrumbledSand(x, y);
8286 static int getSpecialActionElement(int element, int number, int base_element)
8288 return (element != EL_EMPTY ? element :
8289 number != -1 ? base_element + number - 1 :
8293 static int getModifiedActionNumber(int value_old, int operator, int operand,
8294 int value_min, int value_max)
8296 int value_new = (operator == CA_MODE_SET ? operand :
8297 operator == CA_MODE_ADD ? value_old + operand :
8298 operator == CA_MODE_SUBTRACT ? value_old - operand :
8299 operator == CA_MODE_MULTIPLY ? value_old * operand :
8300 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8301 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8304 return (value_new < value_min ? value_min :
8305 value_new > value_max ? value_max :
8309 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8311 struct ElementInfo *ei = &element_info[element];
8312 struct ElementChangeInfo *change = &ei->change_page[page];
8313 int target_element = change->target_element;
8314 int action_type = change->action_type;
8315 int action_mode = change->action_mode;
8316 int action_arg = change->action_arg;
8319 if (!change->has_action)
8322 /* ---------- determine action paramater values -------------------------- */
8324 int level_time_value =
8325 (level.time > 0 ? TimeLeft :
8328 int action_arg_element =
8329 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8330 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8331 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8334 int action_arg_direction =
8335 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8336 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8337 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8338 change->actual_trigger_side :
8339 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8340 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8343 int action_arg_number_min =
8344 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8347 int action_arg_number_max =
8348 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8349 action_type == CA_SET_LEVEL_GEMS ? 999 :
8350 action_type == CA_SET_LEVEL_TIME ? 9999 :
8351 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8352 action_type == CA_SET_CE_VALUE ? 9999 :
8353 action_type == CA_SET_CE_SCORE ? 9999 :
8356 int action_arg_number_reset =
8357 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8358 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8359 action_type == CA_SET_LEVEL_TIME ? level.time :
8360 action_type == CA_SET_LEVEL_SCORE ? 0 :
8362 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8364 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8366 action_type == CA_SET_CE_SCORE ? 0 :
8369 int action_arg_number =
8370 (action_arg <= CA_ARG_MAX ? action_arg :
8371 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8372 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8373 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8374 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8375 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8376 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8377 #if USE_NEW_CUSTOM_VALUE
8378 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8380 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8382 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8383 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8384 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8385 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8386 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8387 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8388 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8389 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8390 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8391 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8392 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8395 int action_arg_number_old =
8396 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8397 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8398 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8399 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8400 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8403 int action_arg_number_new =
8404 getModifiedActionNumber(action_arg_number_old,
8405 action_mode, action_arg_number,
8406 action_arg_number_min, action_arg_number_max);
8408 int trigger_player_bits =
8409 (change->actual_trigger_player >= EL_PLAYER_1 &&
8410 change->actual_trigger_player <= EL_PLAYER_4 ?
8411 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8414 int action_arg_player_bits =
8415 (action_arg >= CA_ARG_PLAYER_1 &&
8416 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8417 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8420 /* ---------- execute action -------------------------------------------- */
8429 /* ---------- level actions ------------------------------------------- */
8431 case CA_RESTART_LEVEL:
8433 game.restart_level = TRUE;
8438 case CA_SHOW_ENVELOPE:
8440 int element = getSpecialActionElement(action_arg_element,
8441 action_arg_number, EL_ENVELOPE_1);
8443 if (IS_ENVELOPE(element))
8444 local_player->show_envelope = element;
8449 case CA_SET_LEVEL_TIME:
8451 if (level.time > 0) /* only modify limited time value */
8453 TimeLeft = action_arg_number_new;
8455 DrawGameValue_Time(TimeLeft);
8457 if (!TimeLeft && setup.time_limit)
8458 for (i = 0; i < MAX_PLAYERS; i++)
8459 KillPlayer(&stored_player[i]);
8465 case CA_SET_LEVEL_SCORE:
8467 local_player->score = action_arg_number_new;
8469 DrawGameValue_Score(local_player->score);
8474 case CA_SET_LEVEL_GEMS:
8476 local_player->gems_still_needed = action_arg_number_new;
8478 DrawGameValue_Emeralds(local_player->gems_still_needed);
8483 #if !USE_PLAYER_GRAVITY
8484 case CA_SET_LEVEL_GRAVITY:
8486 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8487 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8488 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8494 case CA_SET_LEVEL_WIND:
8496 game.wind_direction = action_arg_direction;
8501 /* ---------- player actions ------------------------------------------ */
8503 case CA_MOVE_PLAYER:
8505 /* automatically move to the next field in specified direction */
8506 for (i = 0; i < MAX_PLAYERS; i++)
8507 if (trigger_player_bits & (1 << i))
8508 stored_player[i].programmed_action = action_arg_direction;
8513 case CA_EXIT_PLAYER:
8515 for (i = 0; i < MAX_PLAYERS; i++)
8516 if (action_arg_player_bits & (1 << i))
8517 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8522 case CA_KILL_PLAYER:
8524 for (i = 0; i < MAX_PLAYERS; i++)
8525 if (action_arg_player_bits & (1 << i))
8526 KillPlayer(&stored_player[i]);
8531 case CA_SET_PLAYER_KEYS:
8533 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8534 int element = getSpecialActionElement(action_arg_element,
8535 action_arg_number, EL_KEY_1);
8537 if (IS_KEY(element))
8539 for (i = 0; i < MAX_PLAYERS; i++)
8541 if (trigger_player_bits & (1 << i))
8543 stored_player[i].key[KEY_NR(element)] = key_state;
8546 DrawGameDoorValues();
8548 DrawGameValue_Keys(stored_player[i].key);
8551 redraw_mask |= REDRAW_DOOR_1;
8559 case CA_SET_PLAYER_SPEED:
8561 for (i = 0; i < MAX_PLAYERS; i++)
8563 if (trigger_player_bits & (1 << i))
8565 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8567 if (action_arg == CA_ARG_SPEED_FASTER &&
8568 stored_player[i].cannot_move)
8570 action_arg_number = STEPSIZE_VERY_SLOW;
8572 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8573 action_arg == CA_ARG_SPEED_FASTER)
8575 action_arg_number = 2;
8576 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8579 else if (action_arg == CA_ARG_NUMBER_RESET)
8581 action_arg_number = level.initial_player_stepsize[i];
8585 getModifiedActionNumber(move_stepsize,
8588 action_arg_number_min,
8589 action_arg_number_max);
8592 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8594 /* make sure that value is power of 2 */
8595 move_stepsize = (1 << log_2(move_stepsize));
8597 /* do no immediately change -- the player might just be moving */
8598 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8600 stored_player[i].cannot_move =
8601 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8609 case CA_SET_PLAYER_SHIELD:
8611 for (i = 0; i < MAX_PLAYERS; i++)
8613 if (trigger_player_bits & (1 << i))
8615 if (action_arg == CA_ARG_SHIELD_OFF)
8617 stored_player[i].shield_normal_time_left = 0;
8618 stored_player[i].shield_deadly_time_left = 0;
8620 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8622 stored_player[i].shield_normal_time_left = 999999;
8624 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8626 stored_player[i].shield_normal_time_left = 999999;
8627 stored_player[i].shield_deadly_time_left = 999999;
8635 #if USE_PLAYER_GRAVITY
8636 case CA_SET_PLAYER_GRAVITY:
8638 for (i = 0; i < MAX_PLAYERS; i++)
8640 if (trigger_player_bits & (1 << i))
8642 stored_player[i].gravity =
8643 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8644 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8645 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8646 stored_player[i].gravity);
8654 case CA_SET_PLAYER_ARTWORK:
8656 for (i = 0; i < MAX_PLAYERS; i++)
8658 if (trigger_player_bits & (1 << i))
8660 int artwork_element = action_arg_element;
8662 if (action_arg == CA_ARG_ELEMENT_RESET)
8664 (level.use_artwork_element[i] ? level.artwork_element[i] :
8665 stored_player[i].element_nr);
8667 stored_player[i].artwork_element = artwork_element;
8669 SetPlayerWaiting(&stored_player[i], FALSE);
8671 /* set number of special actions for bored and sleeping animation */
8672 stored_player[i].num_special_action_bored =
8673 get_num_special_action(artwork_element,
8674 ACTION_BORING_1, ACTION_BORING_LAST);
8675 stored_player[i].num_special_action_sleeping =
8676 get_num_special_action(artwork_element,
8677 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8684 /* ---------- CE actions ---------------------------------------------- */
8686 case CA_SET_CE_VALUE:
8688 #if USE_NEW_CUSTOM_VALUE
8689 int last_ce_value = CustomValue[x][y];
8691 CustomValue[x][y] = action_arg_number_new;
8694 printf("::: CE value == %d\n", CustomValue[x][y]);
8697 if (CustomValue[x][y] != last_ce_value)
8699 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8700 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8702 if (CustomValue[x][y] == 0)
8705 printf("::: CE_VALUE_GETS_ZERO\n");
8708 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8709 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8712 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8722 case CA_SET_CE_SCORE:
8724 #if USE_NEW_CUSTOM_VALUE
8725 int last_ce_score = ei->collect_score;
8727 ei->collect_score = action_arg_number_new;
8730 printf("::: CE score == %d\n", ei->collect_score);
8733 if (ei->collect_score != last_ce_score)
8735 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8736 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8738 if (ei->collect_score == 0)
8743 printf("::: CE_SCORE_GETS_ZERO\n");
8746 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8747 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8750 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8755 This is a very special case that seems to be a mixture between
8756 CheckElementChange() and CheckTriggeredElementChange(): while
8757 the first one only affects single elements that are triggered
8758 directly, the second one affects multiple elements in the playfield
8759 that are triggered indirectly by another element. This is a third
8760 case: Changing the CE score always affects multiple identical CEs,
8761 so every affected CE must be checked, not only the single CE for
8762 which the CE score was changed in the first place (as every instance
8763 of that CE shares the same CE score, and therefore also can change)!
8765 SCAN_PLAYFIELD(xx, yy)
8767 if (Feld[xx][yy] == element)
8768 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8769 CE_SCORE_GETS_ZERO);
8780 /* ---------- engine actions ------------------------------------------ */
8782 case CA_SET_ENGINE_SCAN_MODE:
8784 InitPlayfieldScanMode(action_arg);
8794 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8796 int old_element = Feld[x][y];
8797 int new_element = get_element_from_group_element(element);
8798 int previous_move_direction = MovDir[x][y];
8799 #if USE_NEW_CUSTOM_VALUE
8800 int last_ce_value = CustomValue[x][y];
8802 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8803 boolean add_player_onto_element = (new_element_is_player &&
8804 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8805 /* this breaks SnakeBite when a snake is
8806 halfway through a door that closes */
8807 /* NOW FIXED AT LEVEL INIT IN files.c */
8808 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8810 IS_WALKABLE(old_element));
8813 /* check if element under the player changes from accessible to unaccessible
8814 (needed for special case of dropping element which then changes) */
8815 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8816 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8824 if (!add_player_onto_element)
8826 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8827 RemoveMovingField(x, y);
8831 Feld[x][y] = new_element;
8833 #if !USE_GFX_RESET_GFX_ANIMATION
8834 ResetGfxAnimation(x, y);
8835 ResetRandomAnimationValue(x, y);
8838 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8839 MovDir[x][y] = previous_move_direction;
8841 #if USE_NEW_CUSTOM_VALUE
8842 if (element_info[new_element].use_last_ce_value)
8843 CustomValue[x][y] = last_ce_value;
8846 InitField_WithBug1(x, y, FALSE);
8848 new_element = Feld[x][y]; /* element may have changed */
8850 #if USE_GFX_RESET_GFX_ANIMATION
8851 ResetGfxAnimation(x, y);
8852 ResetRandomAnimationValue(x, y);
8855 DrawLevelField(x, y);
8857 if (GFX_CRUMBLED(new_element))
8858 DrawLevelFieldCrumbledSandNeighbours(x, y);
8862 /* check if element under the player changes from accessible to unaccessible
8863 (needed for special case of dropping element which then changes) */
8864 /* (must be checked after creating new element for walkable group elements) */
8865 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8866 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8874 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8875 if (new_element_is_player)
8876 RelocatePlayer(x, y, new_element);
8879 ChangeCount[x][y]++; /* count number of changes in the same frame */
8881 TestIfBadThingTouchesPlayer(x, y);
8882 TestIfPlayerTouchesCustomElement(x, y);
8883 TestIfElementTouchesCustomElement(x, y);
8886 static void CreateField(int x, int y, int element)
8888 CreateFieldExt(x, y, element, FALSE);
8891 static void CreateElementFromChange(int x, int y, int element)
8893 element = GET_VALID_RUNTIME_ELEMENT(element);
8895 #if USE_STOP_CHANGED_ELEMENTS
8896 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8898 int old_element = Feld[x][y];
8900 /* prevent changed element from moving in same engine frame
8901 unless both old and new element can either fall or move */
8902 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8903 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8908 CreateFieldExt(x, y, element, TRUE);
8911 static boolean ChangeElement(int x, int y, int element, int page)
8913 struct ElementInfo *ei = &element_info[element];
8914 struct ElementChangeInfo *change = &ei->change_page[page];
8915 int ce_value = CustomValue[x][y];
8916 int ce_score = ei->collect_score;
8918 int old_element = Feld[x][y];
8920 /* always use default change event to prevent running into a loop */
8921 if (ChangeEvent[x][y] == -1)
8922 ChangeEvent[x][y] = CE_DELAY;
8924 if (ChangeEvent[x][y] == CE_DELAY)
8926 /* reset actual trigger element, trigger player and action element */
8927 change->actual_trigger_element = EL_EMPTY;
8928 change->actual_trigger_player = EL_PLAYER_1;
8929 change->actual_trigger_side = CH_SIDE_NONE;
8930 change->actual_trigger_ce_value = 0;
8931 change->actual_trigger_ce_score = 0;
8934 /* do not change elements more than a specified maximum number of changes */
8935 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8938 ChangeCount[x][y]++; /* count number of changes in the same frame */
8940 if (change->explode)
8947 if (change->use_target_content)
8949 boolean complete_replace = TRUE;
8950 boolean can_replace[3][3];
8953 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8956 boolean is_walkable;
8957 boolean is_diggable;
8958 boolean is_collectible;
8959 boolean is_removable;
8960 boolean is_destructible;
8961 int ex = x + xx - 1;
8962 int ey = y + yy - 1;
8963 int content_element = change->target_content.e[xx][yy];
8966 can_replace[xx][yy] = TRUE;
8968 if (ex == x && ey == y) /* do not check changing element itself */
8971 if (content_element == EL_EMPTY_SPACE)
8973 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8978 if (!IN_LEV_FIELD(ex, ey))
8980 can_replace[xx][yy] = FALSE;
8981 complete_replace = FALSE;
8988 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8989 e = MovingOrBlocked2Element(ex, ey);
8991 is_empty = (IS_FREE(ex, ey) ||
8992 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8994 is_walkable = (is_empty || IS_WALKABLE(e));
8995 is_diggable = (is_empty || IS_DIGGABLE(e));
8996 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8997 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8998 is_removable = (is_diggable || is_collectible);
9000 can_replace[xx][yy] =
9001 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9002 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9003 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9004 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9005 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9006 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9007 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9009 if (!can_replace[xx][yy])
9010 complete_replace = FALSE;
9013 if (!change->only_if_complete || complete_replace)
9015 boolean something_has_changed = FALSE;
9017 if (change->only_if_complete && change->use_random_replace &&
9018 RND(100) < change->random_percentage)
9021 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9023 int ex = x + xx - 1;
9024 int ey = y + yy - 1;
9025 int content_element;
9027 if (can_replace[xx][yy] && (!change->use_random_replace ||
9028 RND(100) < change->random_percentage))
9030 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9031 RemoveMovingField(ex, ey);
9033 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9035 content_element = change->target_content.e[xx][yy];
9036 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9037 ce_value, ce_score);
9039 CreateElementFromChange(ex, ey, target_element);
9041 something_has_changed = TRUE;
9043 /* for symmetry reasons, freeze newly created border elements */
9044 if (ex != x || ey != y)
9045 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9049 if (something_has_changed)
9051 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9052 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9058 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9059 ce_value, ce_score);
9061 if (element == EL_DIAGONAL_GROWING ||
9062 element == EL_DIAGONAL_SHRINKING)
9064 target_element = Store[x][y];
9066 Store[x][y] = EL_EMPTY;
9069 CreateElementFromChange(x, y, target_element);
9071 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9072 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9075 /* this uses direct change before indirect change */
9076 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9081 #if USE_NEW_DELAYED_ACTION
9083 static void HandleElementChange(int x, int y, int page)
9085 int element = MovingOrBlocked2Element(x, y);
9086 struct ElementInfo *ei = &element_info[element];
9087 struct ElementChangeInfo *change = &ei->change_page[page];
9090 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9091 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9094 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9095 x, y, element, element_info[element].token_name);
9096 printf("HandleElementChange(): This should never happen!\n");
9101 /* this can happen with classic bombs on walkable, changing elements */
9102 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9105 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9106 ChangeDelay[x][y] = 0;
9112 if (ChangeDelay[x][y] == 0) /* initialize element change */
9114 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9116 if (change->can_change)
9118 ResetGfxAnimation(x, y);
9119 ResetRandomAnimationValue(x, y);
9121 if (change->pre_change_function)
9122 change->pre_change_function(x, y);
9126 ChangeDelay[x][y]--;
9128 if (ChangeDelay[x][y] != 0) /* continue element change */
9130 if (change->can_change)
9132 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9134 if (IS_ANIMATED(graphic))
9135 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9137 if (change->change_function)
9138 change->change_function(x, y);
9141 else /* finish element change */
9143 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9145 page = ChangePage[x][y];
9146 ChangePage[x][y] = -1;
9148 change = &ei->change_page[page];
9151 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9153 ChangeDelay[x][y] = 1; /* try change after next move step */
9154 ChangePage[x][y] = page; /* remember page to use for change */
9159 if (change->can_change)
9161 if (ChangeElement(x, y, element, page))
9163 if (change->post_change_function)
9164 change->post_change_function(x, y);
9168 if (change->has_action)
9169 ExecuteCustomElementAction(x, y, element, page);
9175 static void HandleElementChange(int x, int y, int page)
9177 int element = MovingOrBlocked2Element(x, y);
9178 struct ElementInfo *ei = &element_info[element];
9179 struct ElementChangeInfo *change = &ei->change_page[page];
9182 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9185 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9186 x, y, element, element_info[element].token_name);
9187 printf("HandleElementChange(): This should never happen!\n");
9192 /* this can happen with classic bombs on walkable, changing elements */
9193 if (!CAN_CHANGE(element))
9196 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9197 ChangeDelay[x][y] = 0;
9203 if (ChangeDelay[x][y] == 0) /* initialize element change */
9205 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9207 ResetGfxAnimation(x, y);
9208 ResetRandomAnimationValue(x, y);
9210 if (change->pre_change_function)
9211 change->pre_change_function(x, y);
9214 ChangeDelay[x][y]--;
9216 if (ChangeDelay[x][y] != 0) /* continue element change */
9218 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9220 if (IS_ANIMATED(graphic))
9221 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9223 if (change->change_function)
9224 change->change_function(x, y);
9226 else /* finish element change */
9228 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9230 page = ChangePage[x][y];
9231 ChangePage[x][y] = -1;
9233 change = &ei->change_page[page];
9236 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9238 ChangeDelay[x][y] = 1; /* try change after next move step */
9239 ChangePage[x][y] = page; /* remember page to use for change */
9244 if (ChangeElement(x, y, element, page))
9246 if (change->post_change_function)
9247 change->post_change_function(x, y);
9254 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9255 int trigger_element,
9261 boolean change_done_any = FALSE;
9262 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9265 if (!(trigger_events[trigger_element][trigger_event]))
9268 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9270 int element = EL_CUSTOM_START + i;
9271 boolean change_done = FALSE;
9274 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9275 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9278 for (p = 0; p < element_info[element].num_change_pages; p++)
9280 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9282 if (change->can_change_or_has_action &&
9283 change->has_event[trigger_event] &&
9284 change->trigger_side & trigger_side &&
9285 change->trigger_player & trigger_player &&
9286 change->trigger_page & trigger_page_bits &&
9287 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9289 change->actual_trigger_element = trigger_element;
9290 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9291 change->actual_trigger_side = trigger_side;
9292 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9293 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9295 if ((change->can_change && !change_done) || change->has_action)
9300 SCAN_PLAYFIELD(x, y)
9302 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9305 if (Feld[x][y] == element)
9307 if (change->can_change && !change_done)
9309 ChangeDelay[x][y] = 1;
9310 ChangeEvent[x][y] = trigger_event;
9312 HandleElementChange(x, y, p);
9314 #if USE_NEW_DELAYED_ACTION
9315 else if (change->has_action)
9317 ExecuteCustomElementAction(x, y, element, p);
9318 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9321 if (change->has_action)
9323 ExecuteCustomElementAction(x, y, element, p);
9324 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9330 if (change->can_change)
9333 change_done_any = TRUE;
9340 return change_done_any;
9343 static boolean CheckElementChangeExt(int x, int y,
9345 int trigger_element,
9350 boolean change_done = FALSE;
9353 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9354 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9357 if (Feld[x][y] == EL_BLOCKED)
9359 Blocked2Moving(x, y, &x, &y);
9360 element = Feld[x][y];
9364 /* check if element has already changed */
9365 if (Feld[x][y] != element)
9368 /* check if element has already changed or is about to change after moving */
9369 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9370 Feld[x][y] != element) ||
9372 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9373 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9374 ChangePage[x][y] != -1)))
9378 for (p = 0; p < element_info[element].num_change_pages; p++)
9380 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9382 boolean check_trigger_element =
9383 (trigger_event == CE_TOUCHING_X ||
9384 trigger_event == CE_HITTING_X ||
9385 trigger_event == CE_HIT_BY_X);
9387 if (change->can_change_or_has_action &&
9388 change->has_event[trigger_event] &&
9389 change->trigger_side & trigger_side &&
9390 change->trigger_player & trigger_player &&
9391 (!check_trigger_element ||
9392 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9394 change->actual_trigger_element = trigger_element;
9395 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9396 change->actual_trigger_side = trigger_side;
9397 change->actual_trigger_ce_value = CustomValue[x][y];
9398 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9400 /* special case: trigger element not at (x,y) position for some events */
9401 if (check_trigger_element)
9413 { 0, 0 }, { 0, 0 }, { 0, 0 },
9417 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9418 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9420 change->actual_trigger_ce_value = CustomValue[xx][yy];
9421 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9424 if (change->can_change && !change_done)
9426 ChangeDelay[x][y] = 1;
9427 ChangeEvent[x][y] = trigger_event;
9429 HandleElementChange(x, y, p);
9433 #if USE_NEW_DELAYED_ACTION
9434 else if (change->has_action)
9436 ExecuteCustomElementAction(x, y, element, p);
9437 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9440 if (change->has_action)
9442 ExecuteCustomElementAction(x, y, element, p);
9443 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9452 static void PlayPlayerSound(struct PlayerInfo *player)
9454 int jx = player->jx, jy = player->jy;
9455 int sound_element = player->artwork_element;
9456 int last_action = player->last_action_waiting;
9457 int action = player->action_waiting;
9459 if (player->is_waiting)
9461 if (action != last_action)
9462 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9464 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9468 if (action != last_action)
9469 StopSound(element_info[sound_element].sound[last_action]);
9471 if (last_action == ACTION_SLEEPING)
9472 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9476 static void PlayAllPlayersSound()
9480 for (i = 0; i < MAX_PLAYERS; i++)
9481 if (stored_player[i].active)
9482 PlayPlayerSound(&stored_player[i]);
9485 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9487 boolean last_waiting = player->is_waiting;
9488 int move_dir = player->MovDir;
9490 player->dir_waiting = move_dir;
9491 player->last_action_waiting = player->action_waiting;
9495 if (!last_waiting) /* not waiting -> waiting */
9497 player->is_waiting = TRUE;
9499 player->frame_counter_bored =
9501 game.player_boring_delay_fixed +
9502 SimpleRND(game.player_boring_delay_random);
9503 player->frame_counter_sleeping =
9505 game.player_sleeping_delay_fixed +
9506 SimpleRND(game.player_sleeping_delay_random);
9509 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9511 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9515 if (game.player_sleeping_delay_fixed +
9516 game.player_sleeping_delay_random > 0 &&
9517 player->anim_delay_counter == 0 &&
9518 player->post_delay_counter == 0 &&
9519 FrameCounter >= player->frame_counter_sleeping)
9520 player->is_sleeping = TRUE;
9521 else if (game.player_boring_delay_fixed +
9522 game.player_boring_delay_random > 0 &&
9523 FrameCounter >= player->frame_counter_bored)
9524 player->is_bored = TRUE;
9526 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9527 player->is_bored ? ACTION_BORING :
9531 if (player->is_sleeping && player->use_murphy)
9533 /* special case for sleeping Murphy when leaning against non-free tile */
9535 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9536 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9537 !IS_MOVING(player->jx - 1, player->jy)))
9539 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9540 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9541 !IS_MOVING(player->jx + 1, player->jy)))
9542 move_dir = MV_RIGHT;
9544 player->is_sleeping = FALSE;
9546 player->dir_waiting = move_dir;
9550 if (player->is_sleeping)
9552 if (player->num_special_action_sleeping > 0)
9554 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9556 int last_special_action = player->special_action_sleeping;
9557 int num_special_action = player->num_special_action_sleeping;
9558 int special_action =
9559 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9560 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9561 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9562 last_special_action + 1 : ACTION_SLEEPING);
9563 int special_graphic =
9564 el_act_dir2img(player->artwork_element, special_action, move_dir);
9566 player->anim_delay_counter =
9567 graphic_info[special_graphic].anim_delay_fixed +
9568 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9569 player->post_delay_counter =
9570 graphic_info[special_graphic].post_delay_fixed +
9571 SimpleRND(graphic_info[special_graphic].post_delay_random);
9573 player->special_action_sleeping = special_action;
9576 if (player->anim_delay_counter > 0)
9578 player->action_waiting = player->special_action_sleeping;
9579 player->anim_delay_counter--;
9581 else if (player->post_delay_counter > 0)
9583 player->post_delay_counter--;
9587 else if (player->is_bored)
9589 if (player->num_special_action_bored > 0)
9591 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9593 int special_action =
9594 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9595 int special_graphic =
9596 el_act_dir2img(player->artwork_element, special_action, move_dir);
9598 player->anim_delay_counter =
9599 graphic_info[special_graphic].anim_delay_fixed +
9600 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9601 player->post_delay_counter =
9602 graphic_info[special_graphic].post_delay_fixed +
9603 SimpleRND(graphic_info[special_graphic].post_delay_random);
9605 player->special_action_bored = special_action;
9608 if (player->anim_delay_counter > 0)
9610 player->action_waiting = player->special_action_bored;
9611 player->anim_delay_counter--;
9613 else if (player->post_delay_counter > 0)
9615 player->post_delay_counter--;
9620 else if (last_waiting) /* waiting -> not waiting */
9622 player->is_waiting = FALSE;
9623 player->is_bored = FALSE;
9624 player->is_sleeping = FALSE;
9626 player->frame_counter_bored = -1;
9627 player->frame_counter_sleeping = -1;
9629 player->anim_delay_counter = 0;
9630 player->post_delay_counter = 0;
9632 player->dir_waiting = player->MovDir;
9633 player->action_waiting = ACTION_DEFAULT;
9635 player->special_action_bored = ACTION_DEFAULT;
9636 player->special_action_sleeping = ACTION_DEFAULT;
9640 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9642 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9643 int left = player_action & JOY_LEFT;
9644 int right = player_action & JOY_RIGHT;
9645 int up = player_action & JOY_UP;
9646 int down = player_action & JOY_DOWN;
9647 int button1 = player_action & JOY_BUTTON_1;
9648 int button2 = player_action & JOY_BUTTON_2;
9649 int dx = (left ? -1 : right ? 1 : 0);
9650 int dy = (up ? -1 : down ? 1 : 0);
9652 if (!player->active || tape.pausing)
9658 snapped = SnapField(player, dx, dy);
9662 dropped = DropElement(player);
9664 moved = MovePlayer(player, dx, dy);
9667 if (tape.single_step && tape.recording && !tape.pausing)
9669 if (button1 || (dropped && !moved))
9671 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9672 SnapField(player, 0, 0); /* stop snapping */
9676 SetPlayerWaiting(player, FALSE);
9678 return player_action;
9682 /* no actions for this player (no input at player's configured device) */
9684 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9685 SnapField(player, 0, 0);
9686 CheckGravityMovementWhenNotMoving(player);
9688 if (player->MovPos == 0)
9689 SetPlayerWaiting(player, TRUE);
9691 if (player->MovPos == 0) /* needed for tape.playing */
9692 player->is_moving = FALSE;
9694 player->is_dropping = FALSE;
9695 player->is_dropping_pressed = FALSE;
9696 player->drop_pressed_delay = 0;
9702 static void CheckLevelTime()
9706 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9708 if (level.native_em_level->lev->home == 0) /* all players at home */
9710 local_player->LevelSolved = TRUE;
9711 AllPlayersGone = TRUE;
9713 level.native_em_level->lev->home = -1;
9716 if (level.native_em_level->ply[0]->alive == 0 &&
9717 level.native_em_level->ply[1]->alive == 0 &&
9718 level.native_em_level->ply[2]->alive == 0 &&
9719 level.native_em_level->ply[3]->alive == 0) /* all dead */
9720 AllPlayersGone = TRUE;
9723 if (TimeFrames >= FRAMES_PER_SECOND)
9728 for (i = 0; i < MAX_PLAYERS; i++)
9730 struct PlayerInfo *player = &stored_player[i];
9732 if (SHIELD_ON(player))
9734 player->shield_normal_time_left--;
9736 if (player->shield_deadly_time_left > 0)
9737 player->shield_deadly_time_left--;
9741 if (!level.use_step_counter)
9749 if (TimeLeft <= 10 && setup.time_limit)
9750 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9752 DrawGameValue_Time(TimeLeft);
9754 if (!TimeLeft && setup.time_limit)
9756 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9757 level.native_em_level->lev->killed_out_of_time = TRUE;
9759 for (i = 0; i < MAX_PLAYERS; i++)
9760 KillPlayer(&stored_player[i]);
9763 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9764 DrawGameValue_Time(TimePlayed);
9766 level.native_em_level->lev->time =
9767 (level.time == 0 ? TimePlayed : TimeLeft);
9770 if (tape.recording || tape.playing)
9771 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9775 void AdvanceFrameAndPlayerCounters(int player_nr)
9780 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9781 FrameCounter, FrameCounter + 1);
9784 /* advance frame counters (global frame counter and time frame counter) */
9788 /* advance player counters (counters for move delay, move animation etc.) */
9789 for (i = 0; i < MAX_PLAYERS; i++)
9791 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9792 int move_delay_value = stored_player[i].move_delay_value;
9793 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9795 if (!advance_player_counters) /* not all players may be affected */
9798 #if USE_NEW_PLAYER_ANIM
9799 if (move_frames == 0) /* less than one move per game frame */
9801 int stepsize = TILEX / move_delay_value;
9802 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9803 int count = (stored_player[i].is_moving ?
9804 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9806 if (count % delay == 0)
9811 stored_player[i].Frame += move_frames;
9813 if (stored_player[i].MovPos != 0)
9814 stored_player[i].StepFrame += move_frames;
9816 if (stored_player[i].move_delay > 0)
9817 stored_player[i].move_delay--;
9819 /* due to bugs in previous versions, counter must count up, not down */
9820 if (stored_player[i].push_delay != -1)
9821 stored_player[i].push_delay++;
9823 if (stored_player[i].drop_delay > 0)
9824 stored_player[i].drop_delay--;
9826 if (stored_player[i].is_dropping_pressed)
9827 stored_player[i].drop_pressed_delay++;
9831 void StartGameActions(boolean init_network_game, boolean record_tape,
9834 unsigned long new_random_seed = InitRND(random_seed);
9837 TapeStartRecording(new_random_seed);
9839 #if defined(NETWORK_AVALIABLE)
9840 if (init_network_game)
9842 SendToServer_StartPlaying();
9853 static unsigned long game_frame_delay = 0;
9854 unsigned long game_frame_delay_value;
9855 byte *recorded_player_action;
9856 byte summarized_player_action = 0;
9857 byte tape_action[MAX_PLAYERS];
9860 if (game.restart_level)
9861 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9863 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9865 if (level.native_em_level->lev->home == 0) /* all players at home */
9867 local_player->LevelSolved = TRUE;
9868 AllPlayersGone = TRUE;
9870 level.native_em_level->lev->home = -1;
9873 if (level.native_em_level->ply[0]->alive == 0 &&
9874 level.native_em_level->ply[1]->alive == 0 &&
9875 level.native_em_level->ply[2]->alive == 0 &&
9876 level.native_em_level->ply[3]->alive == 0) /* all dead */
9877 AllPlayersGone = TRUE;
9880 if (local_player->LevelSolved)
9883 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9886 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9889 game_frame_delay_value =
9890 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9892 if (tape.playing && tape.warp_forward && !tape.pausing)
9893 game_frame_delay_value = 0;
9895 /* ---------- main game synchronization point ---------- */
9897 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9899 if (network_playing && !network_player_action_received)
9901 /* try to get network player actions in time */
9903 #if defined(NETWORK_AVALIABLE)
9904 /* last chance to get network player actions without main loop delay */
9908 /* game was quit by network peer */
9909 if (game_status != GAME_MODE_PLAYING)
9912 if (!network_player_action_received)
9913 return; /* failed to get network player actions in time */
9915 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9921 /* at this point we know that we really continue executing the game */
9924 network_player_action_received = FALSE;
9927 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9929 if (tape.set_centered_player)
9931 game.centered_player_nr_next = tape.centered_player_nr_next;
9932 game.set_centered_player = TRUE;
9935 for (i = 0; i < MAX_PLAYERS; i++)
9937 summarized_player_action |= stored_player[i].action;
9939 if (!network_playing)
9940 stored_player[i].effective_action = stored_player[i].action;
9943 #if defined(NETWORK_AVALIABLE)
9944 if (network_playing)
9945 SendToServer_MovePlayer(summarized_player_action);
9948 if (!options.network && !setup.team_mode)
9949 local_player->effective_action = summarized_player_action;
9951 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9953 for (i = 0; i < MAX_PLAYERS; i++)
9954 stored_player[i].effective_action =
9955 (i == game.centered_player_nr ? summarized_player_action : 0);
9958 if (recorded_player_action != NULL)
9959 for (i = 0; i < MAX_PLAYERS; i++)
9960 stored_player[i].effective_action = recorded_player_action[i];
9962 for (i = 0; i < MAX_PLAYERS; i++)
9964 tape_action[i] = stored_player[i].effective_action;
9966 /* (this can only happen in the R'n'D game engine) */
9967 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9968 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9971 /* only record actions from input devices, but not programmed actions */
9973 TapeRecordAction(tape_action);
9975 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9977 GameActions_EM_Main();
9985 void GameActions_EM_Main()
9987 byte effective_action[MAX_PLAYERS];
9988 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9991 for (i = 0; i < MAX_PLAYERS; i++)
9992 effective_action[i] = stored_player[i].effective_action;
9995 printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
9998 GameActions_EM(effective_action, warp_mode);
10002 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10005 void GameActions_RND()
10007 int magic_wall_x = 0, magic_wall_y = 0;
10008 int i, x, y, element, graphic;
10010 InitPlayfieldScanModeVars();
10012 #if USE_ONE_MORE_CHANGE_PER_FRAME
10013 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10015 SCAN_PLAYFIELD(x, y)
10017 ChangeCount[x][y] = 0;
10018 ChangeEvent[x][y] = -1;
10024 if (game.set_centered_player)
10026 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10028 /* switching to "all players" only possible if all players fit to screen */
10029 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10031 game.centered_player_nr_next = game.centered_player_nr;
10032 game.set_centered_player = FALSE;
10035 /* do not switch focus to non-existing (or non-active) player */
10036 if (game.centered_player_nr_next >= 0 &&
10037 !stored_player[game.centered_player_nr_next].active)
10039 game.centered_player_nr_next = game.centered_player_nr;
10040 game.set_centered_player = FALSE;
10044 if (game.set_centered_player &&
10045 ScreenMovPos == 0) /* screen currently aligned at tile position */
10049 if (game.centered_player_nr_next == -1)
10051 setScreenCenteredToAllPlayers(&sx, &sy);
10055 sx = stored_player[game.centered_player_nr_next].jx;
10056 sy = stored_player[game.centered_player_nr_next].jy;
10059 game.centered_player_nr = game.centered_player_nr_next;
10060 game.set_centered_player = FALSE;
10062 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
10063 DrawGameDoorValues();
10067 for (i = 0; i < MAX_PLAYERS; i++)
10069 int actual_player_action = stored_player[i].effective_action;
10072 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10073 - rnd_equinox_tetrachloride 048
10074 - rnd_equinox_tetrachloride_ii 096
10075 - rnd_emanuel_schmieg 002
10076 - doctor_sloan_ww 001, 020
10078 if (stored_player[i].MovPos == 0)
10079 CheckGravityMovement(&stored_player[i]);
10082 /* overwrite programmed action with tape action */
10083 if (stored_player[i].programmed_action)
10084 actual_player_action = stored_player[i].programmed_action;
10087 PlayerActions(&stored_player[i], actual_player_action);
10089 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
10091 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10092 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10095 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10099 network_player_action_received = FALSE;
10102 ScrollScreen(NULL, SCROLL_GO_ON);
10104 /* for backwards compatibility, the following code emulates a fixed bug that
10105 occured when pushing elements (causing elements that just made their last
10106 pushing step to already (if possible) make their first falling step in the
10107 same game frame, which is bad); this code is also needed to use the famous
10108 "spring push bug" which is used in older levels and might be wanted to be
10109 used also in newer levels, but in this case the buggy pushing code is only
10110 affecting the "spring" element and no other elements */
10112 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10114 for (i = 0; i < MAX_PLAYERS; i++)
10116 struct PlayerInfo *player = &stored_player[i];
10117 int x = player->jx;
10118 int y = player->jy;
10120 if (player->active && player->is_pushing && player->is_moving &&
10122 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10123 Feld[x][y] == EL_SPRING))
10125 ContinueMoving(x, y);
10127 /* continue moving after pushing (this is actually a bug) */
10128 if (!IS_MOVING(x, y))
10130 Stop[x][y] = FALSE;
10137 SCAN_PLAYFIELD(x, y)
10139 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10142 ChangeCount[x][y] = 0;
10143 ChangeEvent[x][y] = -1;
10145 /* this must be handled before main playfield loop */
10146 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10149 if (MovDelay[x][y] <= 0)
10153 #if USE_NEW_SNAP_DELAY
10154 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10157 if (MovDelay[x][y] <= 0)
10160 DrawLevelField(x, y);
10162 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10168 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10170 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10171 printf("GameActions(): This should never happen!\n");
10173 ChangePage[x][y] = -1;
10177 Stop[x][y] = FALSE;
10178 if (WasJustMoving[x][y] > 0)
10179 WasJustMoving[x][y]--;
10180 if (WasJustFalling[x][y] > 0)
10181 WasJustFalling[x][y]--;
10182 if (CheckCollision[x][y] > 0)
10183 CheckCollision[x][y]--;
10187 /* reset finished pushing action (not done in ContinueMoving() to allow
10188 continuous pushing animation for elements with zero push delay) */
10189 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10191 ResetGfxAnimation(x, y);
10192 DrawLevelField(x, y);
10196 if (IS_BLOCKED(x, y))
10200 Blocked2Moving(x, y, &oldx, &oldy);
10201 if (!IS_MOVING(oldx, oldy))
10203 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10204 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10205 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10206 printf("GameActions(): This should never happen!\n");
10213 SCAN_PLAYFIELD(x, y)
10215 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10218 element = Feld[x][y];
10219 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10222 printf("::: %d,%d\n", x, y);
10224 if (element == EL_ROCK)
10225 printf("::: Yo man! Rocks can fall!\n");
10229 ResetGfxFrame(x, y, TRUE);
10231 if (graphic_info[graphic].anim_global_sync)
10232 GfxFrame[x][y] = FrameCounter;
10233 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
10235 int old_gfx_frame = GfxFrame[x][y];
10237 GfxFrame[x][y] = CustomValue[x][y];
10240 if (GfxFrame[x][y] != old_gfx_frame)
10242 DrawLevelGraphicAnimation(x, y, graphic);
10244 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
10246 int old_gfx_frame = GfxFrame[x][y];
10248 GfxFrame[x][y] = element_info[element].collect_score;
10251 if (GfxFrame[x][y] != old_gfx_frame)
10253 DrawLevelGraphicAnimation(x, y, graphic);
10255 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
10257 int old_gfx_frame = GfxFrame[x][y];
10259 GfxFrame[x][y] = ChangeDelay[x][y];
10262 if (GfxFrame[x][y] != old_gfx_frame)
10264 DrawLevelGraphicAnimation(x, y, graphic);
10268 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10269 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10270 ResetRandomAnimationValue(x, y);
10272 SetRandomAnimationValue(x, y);
10274 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10276 if (IS_INACTIVE(element))
10278 if (IS_ANIMATED(graphic))
10279 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10284 /* this may take place after moving, so 'element' may have changed */
10285 if (IS_CHANGING(x, y) &&
10286 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10288 int page = element_info[element].event_page_nr[CE_DELAY];
10290 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
10294 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10298 if (element == EL_CUSTOM_255)
10299 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10303 HandleElementChange(x, y, page);
10305 if (CAN_CHANGE(element))
10306 HandleElementChange(x, y, page);
10308 if (HAS_ACTION(element))
10309 ExecuteCustomElementAction(x, y, element, page);
10314 element = Feld[x][y];
10315 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10318 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10322 element = Feld[x][y];
10323 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10325 if (IS_ANIMATED(graphic) &&
10326 !IS_MOVING(x, y) &&
10328 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10330 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10331 EdelsteinFunkeln(x, y);
10333 else if ((element == EL_ACID ||
10334 element == EL_EXIT_OPEN ||
10335 element == EL_SP_EXIT_OPEN ||
10336 element == EL_SP_TERMINAL ||
10337 element == EL_SP_TERMINAL_ACTIVE ||
10338 element == EL_EXTRA_TIME ||
10339 element == EL_SHIELD_NORMAL ||
10340 element == EL_SHIELD_DEADLY) &&
10341 IS_ANIMATED(graphic))
10342 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10343 else if (IS_MOVING(x, y))
10344 ContinueMoving(x, y);
10345 else if (IS_ACTIVE_BOMB(element))
10346 CheckDynamite(x, y);
10347 else if (element == EL_AMOEBA_GROWING)
10348 AmoebeWaechst(x, y);
10349 else if (element == EL_AMOEBA_SHRINKING)
10350 AmoebaDisappearing(x, y);
10352 #if !USE_NEW_AMOEBA_CODE
10353 else if (IS_AMOEBALIVE(element))
10354 AmoebeAbleger(x, y);
10357 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10359 else if (element == EL_EXIT_CLOSED)
10361 else if (element == EL_SP_EXIT_CLOSED)
10363 else if (element == EL_EXPANDABLE_WALL_GROWING)
10364 MauerWaechst(x, y);
10365 else if (element == EL_EXPANDABLE_WALL ||
10366 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10367 element == EL_EXPANDABLE_WALL_VERTICAL ||
10368 element == EL_EXPANDABLE_WALL_ANY ||
10369 element == EL_BD_EXPANDABLE_WALL)
10370 MauerAbleger(x, y);
10371 else if (element == EL_FLAMES)
10372 CheckForDragon(x, y);
10373 else if (element == EL_EXPLOSION)
10374 ; /* drawing of correct explosion animation is handled separately */
10375 else if (element == EL_ELEMENT_SNAPPING ||
10376 element == EL_DIAGONAL_SHRINKING ||
10377 element == EL_DIAGONAL_GROWING)
10380 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10382 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10385 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10386 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10389 if (element == EL_CUSTOM_255 ||
10390 element == EL_CUSTOM_256)
10391 DrawLevelGraphicAnimation(x, y, graphic);
10394 if (IS_BELT_ACTIVE(element))
10395 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10397 if (game.magic_wall_active)
10399 int jx = local_player->jx, jy = local_player->jy;
10401 /* play the element sound at the position nearest to the player */
10402 if ((element == EL_MAGIC_WALL_FULL ||
10403 element == EL_MAGIC_WALL_ACTIVE ||
10404 element == EL_MAGIC_WALL_EMPTYING ||
10405 element == EL_BD_MAGIC_WALL_FULL ||
10406 element == EL_BD_MAGIC_WALL_ACTIVE ||
10407 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10408 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10416 #if USE_NEW_AMOEBA_CODE
10417 /* new experimental amoeba growth stuff */
10418 if (!(FrameCounter % 8))
10420 static unsigned long random = 1684108901;
10422 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10424 x = RND(lev_fieldx);
10425 y = RND(lev_fieldy);
10426 element = Feld[x][y];
10428 if (!IS_PLAYER(x,y) &&
10429 (element == EL_EMPTY ||
10430 CAN_GROW_INTO(element) ||
10431 element == EL_QUICKSAND_EMPTY ||
10432 element == EL_ACID_SPLASH_LEFT ||
10433 element == EL_ACID_SPLASH_RIGHT))
10435 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10436 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10437 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10438 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10439 Feld[x][y] = EL_AMOEBA_DROP;
10442 random = random * 129 + 1;
10448 if (game.explosions_delayed)
10451 game.explosions_delayed = FALSE;
10454 SCAN_PLAYFIELD(x, y)
10456 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10459 element = Feld[x][y];
10461 if (ExplodeField[x][y])
10462 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10463 else if (element == EL_EXPLOSION)
10464 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10466 ExplodeField[x][y] = EX_TYPE_NONE;
10469 game.explosions_delayed = TRUE;
10472 if (game.magic_wall_active)
10474 if (!(game.magic_wall_time_left % 4))
10476 int element = Feld[magic_wall_x][magic_wall_y];
10478 if (element == EL_BD_MAGIC_WALL_FULL ||
10479 element == EL_BD_MAGIC_WALL_ACTIVE ||
10480 element == EL_BD_MAGIC_WALL_EMPTYING)
10481 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10483 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10486 if (game.magic_wall_time_left > 0)
10488 game.magic_wall_time_left--;
10489 if (!game.magic_wall_time_left)
10492 SCAN_PLAYFIELD(x, y)
10494 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10497 element = Feld[x][y];
10499 if (element == EL_MAGIC_WALL_ACTIVE ||
10500 element == EL_MAGIC_WALL_FULL)
10502 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10503 DrawLevelField(x, y);
10505 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10506 element == EL_BD_MAGIC_WALL_FULL)
10508 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10509 DrawLevelField(x, y);
10513 game.magic_wall_active = FALSE;
10518 if (game.light_time_left > 0)
10520 game.light_time_left--;
10522 if (game.light_time_left == 0)
10523 RedrawAllLightSwitchesAndInvisibleElements();
10526 if (game.timegate_time_left > 0)
10528 game.timegate_time_left--;
10530 if (game.timegate_time_left == 0)
10531 CloseAllOpenTimegates();
10534 if (game.lenses_time_left > 0)
10536 game.lenses_time_left--;
10538 if (game.lenses_time_left == 0)
10539 RedrawAllInvisibleElementsForLenses();
10542 if (game.magnify_time_left > 0)
10544 game.magnify_time_left--;
10546 if (game.magnify_time_left == 0)
10547 RedrawAllInvisibleElementsForMagnifier();
10550 for (i = 0; i < MAX_PLAYERS; i++)
10552 struct PlayerInfo *player = &stored_player[i];
10554 if (SHIELD_ON(player))
10556 if (player->shield_deadly_time_left)
10557 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10558 else if (player->shield_normal_time_left)
10559 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10566 PlayAllPlayersSound();
10568 if (options.debug) /* calculate frames per second */
10570 static unsigned long fps_counter = 0;
10571 static int fps_frames = 0;
10572 unsigned long fps_delay_ms = Counter() - fps_counter;
10576 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10578 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10581 fps_counter = Counter();
10584 redraw_mask |= REDRAW_FPS;
10587 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10589 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10591 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10593 local_player->show_envelope = 0;
10596 /* use random number generator in every frame to make it less predictable */
10597 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10601 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10603 int min_x = x, min_y = y, max_x = x, max_y = y;
10606 for (i = 0; i < MAX_PLAYERS; i++)
10608 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10610 if (!stored_player[i].active || &stored_player[i] == player)
10613 min_x = MIN(min_x, jx);
10614 min_y = MIN(min_y, jy);
10615 max_x = MAX(max_x, jx);
10616 max_y = MAX(max_y, jy);
10619 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10622 static boolean AllPlayersInVisibleScreen()
10626 for (i = 0; i < MAX_PLAYERS; i++)
10628 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10630 if (!stored_player[i].active)
10633 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10640 void ScrollLevel(int dx, int dy)
10642 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10645 BlitBitmap(drawto_field, drawto_field,
10646 FX + TILEX * (dx == -1) - softscroll_offset,
10647 FY + TILEY * (dy == -1) - softscroll_offset,
10648 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10649 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10650 FX + TILEX * (dx == 1) - softscroll_offset,
10651 FY + TILEY * (dy == 1) - softscroll_offset);
10655 x = (dx == 1 ? BX1 : BX2);
10656 for (y = BY1; y <= BY2; y++)
10657 DrawScreenField(x, y);
10662 y = (dy == 1 ? BY1 : BY2);
10663 for (x = BX1; x <= BX2; x++)
10664 DrawScreenField(x, y);
10667 redraw_mask |= REDRAW_FIELD;
10670 static boolean canFallDown(struct PlayerInfo *player)
10672 int jx = player->jx, jy = player->jy;
10674 return (IN_LEV_FIELD(jx, jy + 1) &&
10675 (IS_FREE(jx, jy + 1) ||
10676 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10677 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10678 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10681 static boolean canPassField(int x, int y, int move_dir)
10683 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10684 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10685 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10686 int nextx = x + dx;
10687 int nexty = y + dy;
10688 int element = Feld[x][y];
10690 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10691 !CAN_MOVE(element) &&
10692 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10693 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10694 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10697 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10699 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10700 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10701 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10705 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10706 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10707 (IS_DIGGABLE(Feld[newx][newy]) ||
10708 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10709 canPassField(newx, newy, move_dir)));
10712 static void CheckGravityMovement(struct PlayerInfo *player)
10714 #if USE_PLAYER_GRAVITY
10715 if (player->gravity && !player->programmed_action)
10717 if (game.gravity && !player->programmed_action)
10720 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10721 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10722 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10723 int jx = player->jx, jy = player->jy;
10724 boolean player_is_moving_to_valid_field =
10725 (!player_is_snapping &&
10726 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10727 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10728 boolean player_can_fall_down = canFallDown(player);
10730 if (player_can_fall_down &&
10731 !player_is_moving_to_valid_field)
10732 player->programmed_action = MV_DOWN;
10736 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10738 return CheckGravityMovement(player);
10740 #if USE_PLAYER_GRAVITY
10741 if (player->gravity && !player->programmed_action)
10743 if (game.gravity && !player->programmed_action)
10746 int jx = player->jx, jy = player->jy;
10747 boolean field_under_player_is_free =
10748 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10749 boolean player_is_standing_on_valid_field =
10750 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10751 (IS_WALKABLE(Feld[jx][jy]) &&
10752 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10754 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10755 player->programmed_action = MV_DOWN;
10760 MovePlayerOneStep()
10761 -----------------------------------------------------------------------------
10762 dx, dy: direction (non-diagonal) to try to move the player to
10763 real_dx, real_dy: direction as read from input device (can be diagonal)
10766 boolean MovePlayerOneStep(struct PlayerInfo *player,
10767 int dx, int dy, int real_dx, int real_dy)
10769 int jx = player->jx, jy = player->jy;
10770 int new_jx = jx + dx, new_jy = jy + dy;
10771 #if !USE_FIXED_DONT_RUN_INTO
10775 boolean player_can_move = !player->cannot_move;
10777 if (!player->active || (!dx && !dy))
10778 return MP_NO_ACTION;
10780 player->MovDir = (dx < 0 ? MV_LEFT :
10781 dx > 0 ? MV_RIGHT :
10783 dy > 0 ? MV_DOWN : MV_NONE);
10785 if (!IN_LEV_FIELD(new_jx, new_jy))
10786 return MP_NO_ACTION;
10788 if (!player_can_move)
10791 if (player->MovPos == 0)
10793 player->is_moving = FALSE;
10794 player->is_digging = FALSE;
10795 player->is_collecting = FALSE;
10796 player->is_snapping = FALSE;
10797 player->is_pushing = FALSE;
10800 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10801 SnapField(player, 0, 0);
10805 return MP_NO_ACTION;
10810 if (!options.network && game.centered_player_nr == -1 &&
10811 !AllPlayersInSight(player, new_jx, new_jy))
10812 return MP_NO_ACTION;
10814 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10815 return MP_NO_ACTION;
10818 #if !USE_FIXED_DONT_RUN_INTO
10819 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10821 /* (moved to DigField()) */
10822 if (player_can_move && DONT_RUN_INTO(element))
10824 if (element == EL_ACID && dx == 0 && dy == 1)
10826 SplashAcid(new_jx, new_jy);
10827 Feld[jx][jy] = EL_PLAYER_1;
10828 InitMovingField(jx, jy, MV_DOWN);
10829 Store[jx][jy] = EL_ACID;
10830 ContinueMoving(jx, jy);
10831 BuryPlayer(player);
10834 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10840 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10842 #if USE_FIXED_DONT_RUN_INTO
10843 if (can_move == MP_DONT_RUN_INTO)
10847 if (can_move != MP_MOVING)
10850 #if USE_FIXED_DONT_RUN_INTO
10853 /* check if DigField() has caused relocation of the player */
10854 if (player->jx != jx || player->jy != jy)
10855 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10857 StorePlayer[jx][jy] = 0;
10858 player->last_jx = jx;
10859 player->last_jy = jy;
10860 player->jx = new_jx;
10861 player->jy = new_jy;
10862 StorePlayer[new_jx][new_jy] = player->element_nr;
10864 if (player->move_delay_value_next != -1)
10866 player->move_delay_value = player->move_delay_value_next;
10867 player->move_delay_value_next = -1;
10871 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10873 player->step_counter++;
10875 PlayerVisit[jx][jy] = FrameCounter;
10877 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10878 player->is_moving = TRUE;
10882 /* should better be called in MovePlayer(), but this breaks some tapes */
10883 ScrollPlayer(player, SCROLL_INIT);
10889 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10891 int jx = player->jx, jy = player->jy;
10892 int old_jx = jx, old_jy = jy;
10893 int moved = MP_NO_ACTION;
10895 if (!player->active)
10900 if (player->MovPos == 0)
10902 player->is_moving = FALSE;
10903 player->is_digging = FALSE;
10904 player->is_collecting = FALSE;
10905 player->is_snapping = FALSE;
10906 player->is_pushing = FALSE;
10912 if (player->move_delay > 0)
10915 player->move_delay = -1; /* set to "uninitialized" value */
10917 /* store if player is automatically moved to next field */
10918 player->is_auto_moving = (player->programmed_action != MV_NONE);
10920 /* remove the last programmed player action */
10921 player->programmed_action = 0;
10923 if (player->MovPos)
10925 /* should only happen if pre-1.2 tape recordings are played */
10926 /* this is only for backward compatibility */
10928 int original_move_delay_value = player->move_delay_value;
10931 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10935 /* scroll remaining steps with finest movement resolution */
10936 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10938 while (player->MovPos)
10940 ScrollPlayer(player, SCROLL_GO_ON);
10941 ScrollScreen(NULL, SCROLL_GO_ON);
10943 AdvanceFrameAndPlayerCounters(player->index_nr);
10949 player->move_delay_value = original_move_delay_value;
10952 player->is_active = FALSE;
10954 if (player->last_move_dir & MV_HORIZONTAL)
10956 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10957 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10961 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10962 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10965 #if USE_FIXED_BORDER_RUNNING_GFX
10966 if (!moved && !player->is_active)
10968 player->is_moving = FALSE;
10969 player->is_digging = FALSE;
10970 player->is_collecting = FALSE;
10971 player->is_snapping = FALSE;
10972 player->is_pushing = FALSE;
10980 if (moved & MP_MOVING && !ScreenMovPos &&
10981 (player->index_nr == game.centered_player_nr ||
10982 game.centered_player_nr == -1))
10984 if (moved & MP_MOVING && !ScreenMovPos &&
10985 (player == local_player || !options.network))
10988 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10989 int offset = (setup.scroll_delay ? 3 : 0);
10991 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10993 /* actual player has left the screen -- scroll in that direction */
10994 if (jx != old_jx) /* player has moved horizontally */
10995 scroll_x += (jx - old_jx);
10996 else /* player has moved vertically */
10997 scroll_y += (jy - old_jy);
11001 if (jx != old_jx) /* player has moved horizontally */
11003 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11004 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11005 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11007 /* don't scroll over playfield boundaries */
11008 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11009 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11011 /* don't scroll more than one field at a time */
11012 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11014 /* don't scroll against the player's moving direction */
11015 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11016 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11017 scroll_x = old_scroll_x;
11019 else /* player has moved vertically */
11021 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11022 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11023 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11025 /* don't scroll over playfield boundaries */
11026 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11027 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11029 /* don't scroll more than one field at a time */
11030 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11032 /* don't scroll against the player's moving direction */
11033 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11034 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11035 scroll_y = old_scroll_y;
11039 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11042 if (!options.network && game.centered_player_nr == -1 &&
11043 !AllPlayersInVisibleScreen())
11045 scroll_x = old_scroll_x;
11046 scroll_y = old_scroll_y;
11050 if (!options.network && !AllPlayersInVisibleScreen())
11052 scroll_x = old_scroll_x;
11053 scroll_y = old_scroll_y;
11058 ScrollScreen(player, SCROLL_INIT);
11059 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11064 player->StepFrame = 0;
11066 if (moved & MP_MOVING)
11068 if (old_jx != jx && old_jy == jy)
11069 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11070 else if (old_jx == jx && old_jy != jy)
11071 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11073 DrawLevelField(jx, jy); /* for "crumbled sand" */
11075 player->last_move_dir = player->MovDir;
11076 player->is_moving = TRUE;
11077 player->is_snapping = FALSE;
11078 player->is_switching = FALSE;
11079 player->is_dropping = FALSE;
11080 player->is_dropping_pressed = FALSE;
11081 player->drop_pressed_delay = 0;
11084 /* should better be called here than above, but this breaks some tapes */
11085 ScrollPlayer(player, SCROLL_INIT);
11090 CheckGravityMovementWhenNotMoving(player);
11092 player->is_moving = FALSE;
11094 /* at this point, the player is allowed to move, but cannot move right now
11095 (e.g. because of something blocking the way) -- ensure that the player
11096 is also allowed to move in the next frame (in old versions before 3.1.1,
11097 the player was forced to wait again for eight frames before next try) */
11099 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11100 player->move_delay = 0; /* allow direct movement in the next frame */
11103 if (player->move_delay == -1) /* not yet initialized by DigField() */
11104 player->move_delay = player->move_delay_value;
11106 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11108 TestIfPlayerTouchesBadThing(jx, jy);
11109 TestIfPlayerTouchesCustomElement(jx, jy);
11112 if (!player->active)
11113 RemovePlayer(player);
11118 void ScrollPlayer(struct PlayerInfo *player, int mode)
11120 int jx = player->jx, jy = player->jy;
11121 int last_jx = player->last_jx, last_jy = player->last_jy;
11122 int move_stepsize = TILEX / player->move_delay_value;
11124 #if USE_NEW_PLAYER_SPEED
11125 if (!player->active)
11128 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11131 if (!player->active || player->MovPos == 0)
11135 if (mode == SCROLL_INIT)
11137 player->actual_frame_counter = FrameCounter;
11138 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11140 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11141 Feld[last_jx][last_jy] == EL_EMPTY)
11143 int last_field_block_delay = 0; /* start with no blocking at all */
11144 int block_delay_adjustment = player->block_delay_adjustment;
11146 /* if player blocks last field, add delay for exactly one move */
11147 if (player->block_last_field)
11149 last_field_block_delay += player->move_delay_value;
11151 /* when blocking enabled, prevent moving up despite gravity */
11152 #if USE_PLAYER_GRAVITY
11153 if (player->gravity && player->MovDir == MV_UP)
11154 block_delay_adjustment = -1;
11156 if (game.gravity && player->MovDir == MV_UP)
11157 block_delay_adjustment = -1;
11161 /* add block delay adjustment (also possible when not blocking) */
11162 last_field_block_delay += block_delay_adjustment;
11164 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11165 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11168 #if USE_NEW_PLAYER_SPEED
11169 if (player->MovPos != 0) /* player has not yet reached destination */
11175 else if (!FrameReached(&player->actual_frame_counter, 1))
11179 printf("::: player->MovPos: %d -> %d\n",
11181 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
11184 #if USE_NEW_PLAYER_SPEED
11185 if (player->MovPos != 0)
11187 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11188 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11190 /* before DrawPlayer() to draw correct player graphic for this case */
11191 if (player->MovPos == 0)
11192 CheckGravityMovement(player);
11195 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11196 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11198 /* before DrawPlayer() to draw correct player graphic for this case */
11199 if (player->MovPos == 0)
11200 CheckGravityMovement(player);
11203 if (player->MovPos == 0) /* player reached destination field */
11206 printf("::: player reached destination field\n");
11209 if (player->move_delay_reset_counter > 0)
11211 player->move_delay_reset_counter--;
11213 if (player->move_delay_reset_counter == 0)
11215 /* continue with normal speed after quickly moving through gate */
11216 HALVE_PLAYER_SPEED(player);
11218 /* be able to make the next move without delay */
11219 player->move_delay = 0;
11223 player->last_jx = jx;
11224 player->last_jy = jy;
11226 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11227 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11228 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11230 DrawPlayer(player); /* needed here only to cleanup last field */
11231 RemovePlayer(player);
11233 if (local_player->friends_still_needed == 0 ||
11234 IS_SP_ELEMENT(Feld[jx][jy]))
11235 player->LevelSolved = player->GameOver = TRUE;
11238 /* this breaks one level: "machine", level 000 */
11240 int move_direction = player->MovDir;
11241 int enter_side = MV_DIR_OPPOSITE(move_direction);
11242 int leave_side = move_direction;
11243 int old_jx = last_jx;
11244 int old_jy = last_jy;
11245 int old_element = Feld[old_jx][old_jy];
11246 int new_element = Feld[jx][jy];
11248 if (IS_CUSTOM_ELEMENT(old_element))
11249 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11251 player->index_bit, leave_side);
11253 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11254 CE_PLAYER_LEAVES_X,
11255 player->index_bit, leave_side);
11257 if (IS_CUSTOM_ELEMENT(new_element))
11258 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11259 player->index_bit, enter_side);
11261 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11262 CE_PLAYER_ENTERS_X,
11263 player->index_bit, enter_side);
11265 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11266 CE_MOVE_OF_X, move_direction);
11269 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11271 TestIfPlayerTouchesBadThing(jx, jy);
11272 TestIfPlayerTouchesCustomElement(jx, jy);
11274 /* needed because pushed element has not yet reached its destination,
11275 so it would trigger a change event at its previous field location */
11276 if (!player->is_pushing)
11277 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11279 if (!player->active)
11280 RemovePlayer(player);
11283 if (level.use_step_counter)
11293 if (TimeLeft <= 10 && setup.time_limit)
11294 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11296 DrawGameValue_Time(TimeLeft);
11298 if (!TimeLeft && setup.time_limit)
11299 for (i = 0; i < MAX_PLAYERS; i++)
11300 KillPlayer(&stored_player[i]);
11302 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11303 DrawGameValue_Time(TimePlayed);
11306 if (tape.single_step && tape.recording && !tape.pausing &&
11307 !player->programmed_action)
11308 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11312 void ScrollScreen(struct PlayerInfo *player, int mode)
11314 static unsigned long screen_frame_counter = 0;
11316 if (mode == SCROLL_INIT)
11318 /* set scrolling step size according to actual player's moving speed */
11319 ScrollStepSize = TILEX / player->move_delay_value;
11321 screen_frame_counter = FrameCounter;
11322 ScreenMovDir = player->MovDir;
11323 ScreenMovPos = player->MovPos;
11324 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11327 else if (!FrameReached(&screen_frame_counter, 1))
11332 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11333 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11334 redraw_mask |= REDRAW_FIELD;
11337 ScreenMovDir = MV_NONE;
11340 void TestIfPlayerTouchesCustomElement(int x, int y)
11342 static int xy[4][2] =
11349 static int trigger_sides[4][2] =
11351 /* center side border side */
11352 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11353 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11354 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11355 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11357 static int touch_dir[4] =
11359 MV_LEFT | MV_RIGHT,
11364 int center_element = Feld[x][y]; /* should always be non-moving! */
11367 for (i = 0; i < NUM_DIRECTIONS; i++)
11369 int xx = x + xy[i][0];
11370 int yy = y + xy[i][1];
11371 int center_side = trigger_sides[i][0];
11372 int border_side = trigger_sides[i][1];
11373 int border_element;
11375 if (!IN_LEV_FIELD(xx, yy))
11378 if (IS_PLAYER(x, y))
11380 struct PlayerInfo *player = PLAYERINFO(x, y);
11382 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11383 border_element = Feld[xx][yy]; /* may be moving! */
11384 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11385 border_element = Feld[xx][yy];
11386 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11387 border_element = MovingOrBlocked2Element(xx, yy);
11389 continue; /* center and border element do not touch */
11391 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11392 player->index_bit, border_side);
11393 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11394 CE_PLAYER_TOUCHES_X,
11395 player->index_bit, border_side);
11397 else if (IS_PLAYER(xx, yy))
11399 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11401 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11403 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11404 continue; /* center and border element do not touch */
11407 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11408 player->index_bit, center_side);
11409 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11410 CE_PLAYER_TOUCHES_X,
11411 player->index_bit, center_side);
11417 #if USE_ELEMENT_TOUCHING_BUGFIX
11419 void TestIfElementTouchesCustomElement(int x, int y)
11421 static int xy[4][2] =
11428 static int trigger_sides[4][2] =
11430 /* center side border side */
11431 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11432 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11433 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11434 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11436 static int touch_dir[4] =
11438 MV_LEFT | MV_RIGHT,
11443 boolean change_center_element = FALSE;
11444 int center_element = Feld[x][y]; /* should always be non-moving! */
11445 int border_element_old[NUM_DIRECTIONS];
11448 for (i = 0; i < NUM_DIRECTIONS; i++)
11450 int xx = x + xy[i][0];
11451 int yy = y + xy[i][1];
11452 int border_element;
11454 border_element_old[i] = -1;
11456 if (!IN_LEV_FIELD(xx, yy))
11459 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11460 border_element = Feld[xx][yy]; /* may be moving! */
11461 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11462 border_element = Feld[xx][yy];
11463 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11464 border_element = MovingOrBlocked2Element(xx, yy);
11466 continue; /* center and border element do not touch */
11468 border_element_old[i] = border_element;
11471 for (i = 0; i < NUM_DIRECTIONS; i++)
11473 int xx = x + xy[i][0];
11474 int yy = y + xy[i][1];
11475 int center_side = trigger_sides[i][0];
11476 int border_element = border_element_old[i];
11478 if (border_element == -1)
11481 /* check for change of border element */
11482 CheckElementChangeBySide(xx, yy, border_element, center_element,
11483 CE_TOUCHING_X, center_side);
11486 for (i = 0; i < NUM_DIRECTIONS; i++)
11488 int border_side = trigger_sides[i][1];
11489 int border_element = border_element_old[i];
11491 if (border_element == -1)
11494 /* check for change of center element (but change it only once) */
11495 if (!change_center_element)
11496 change_center_element =
11497 CheckElementChangeBySide(x, y, center_element, border_element,
11498 CE_TOUCHING_X, border_side);
11504 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11506 static int xy[4][2] =
11513 static int trigger_sides[4][2] =
11515 /* center side border side */
11516 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11517 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11518 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11519 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11521 static int touch_dir[4] =
11523 MV_LEFT | MV_RIGHT,
11528 boolean change_center_element = FALSE;
11529 int center_element = Feld[x][y]; /* should always be non-moving! */
11532 for (i = 0; i < NUM_DIRECTIONS; i++)
11534 int xx = x + xy[i][0];
11535 int yy = y + xy[i][1];
11536 int center_side = trigger_sides[i][0];
11537 int border_side = trigger_sides[i][1];
11538 int border_element;
11540 if (!IN_LEV_FIELD(xx, yy))
11543 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11544 border_element = Feld[xx][yy]; /* may be moving! */
11545 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11546 border_element = Feld[xx][yy];
11547 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11548 border_element = MovingOrBlocked2Element(xx, yy);
11550 continue; /* center and border element do not touch */
11552 /* check for change of center element (but change it only once) */
11553 if (!change_center_element)
11554 change_center_element =
11555 CheckElementChangeBySide(x, y, center_element, border_element,
11556 CE_TOUCHING_X, border_side);
11558 /* check for change of border element */
11559 CheckElementChangeBySide(xx, yy, border_element, center_element,
11560 CE_TOUCHING_X, center_side);
11566 void TestIfElementHitsCustomElement(int x, int y, int direction)
11568 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11569 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11570 int hitx = x + dx, hity = y + dy;
11571 int hitting_element = Feld[x][y];
11572 int touched_element;
11574 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11577 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11578 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11580 if (IN_LEV_FIELD(hitx, hity))
11582 int opposite_direction = MV_DIR_OPPOSITE(direction);
11583 int hitting_side = direction;
11584 int touched_side = opposite_direction;
11585 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11586 MovDir[hitx][hity] != direction ||
11587 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11593 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11594 CE_HITTING_X, touched_side);
11596 CheckElementChangeBySide(hitx, hity, touched_element,
11597 hitting_element, CE_HIT_BY_X, hitting_side);
11599 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11600 CE_HIT_BY_SOMETHING, opposite_direction);
11604 /* "hitting something" is also true when hitting the playfield border */
11605 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11606 CE_HITTING_SOMETHING, direction);
11610 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11612 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11613 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11614 int hitx = x + dx, hity = y + dy;
11615 int hitting_element = Feld[x][y];
11616 int touched_element;
11618 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11619 !IS_FREE(hitx, hity) &&
11620 (!IS_MOVING(hitx, hity) ||
11621 MovDir[hitx][hity] != direction ||
11622 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11625 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11629 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11633 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11634 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11636 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11637 EP_CAN_SMASH_EVERYTHING, direction);
11639 if (IN_LEV_FIELD(hitx, hity))
11641 int opposite_direction = MV_DIR_OPPOSITE(direction);
11642 int hitting_side = direction;
11643 int touched_side = opposite_direction;
11645 int touched_element = MovingOrBlocked2Element(hitx, hity);
11648 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11649 MovDir[hitx][hity] != direction ||
11650 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11659 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11660 CE_SMASHED_BY_SOMETHING, opposite_direction);
11662 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11663 CE_OTHER_IS_SMASHING, touched_side);
11665 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11666 CE_OTHER_GETS_SMASHED, hitting_side);
11672 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11674 int i, kill_x = -1, kill_y = -1;
11676 int bad_element = -1;
11677 static int test_xy[4][2] =
11684 static int test_dir[4] =
11692 for (i = 0; i < NUM_DIRECTIONS; i++)
11694 int test_x, test_y, test_move_dir, test_element;
11696 test_x = good_x + test_xy[i][0];
11697 test_y = good_y + test_xy[i][1];
11699 if (!IN_LEV_FIELD(test_x, test_y))
11703 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11705 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11707 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11708 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11710 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11711 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11715 bad_element = test_element;
11721 if (kill_x != -1 || kill_y != -1)
11723 if (IS_PLAYER(good_x, good_y))
11725 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11727 if (player->shield_deadly_time_left > 0 &&
11728 !IS_INDESTRUCTIBLE(bad_element))
11729 Bang(kill_x, kill_y);
11730 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11731 KillPlayer(player);
11734 Bang(good_x, good_y);
11738 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11740 int i, kill_x = -1, kill_y = -1;
11741 int bad_element = Feld[bad_x][bad_y];
11742 static int test_xy[4][2] =
11749 static int touch_dir[4] =
11751 MV_LEFT | MV_RIGHT,
11756 static int test_dir[4] =
11764 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11767 for (i = 0; i < NUM_DIRECTIONS; i++)
11769 int test_x, test_y, test_move_dir, test_element;
11771 test_x = bad_x + test_xy[i][0];
11772 test_y = bad_y + test_xy[i][1];
11773 if (!IN_LEV_FIELD(test_x, test_y))
11777 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11779 test_element = Feld[test_x][test_y];
11781 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11782 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11784 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11785 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11787 /* good thing is player or penguin that does not move away */
11788 if (IS_PLAYER(test_x, test_y))
11790 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11792 if (bad_element == EL_ROBOT && player->is_moving)
11793 continue; /* robot does not kill player if he is moving */
11795 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11797 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11798 continue; /* center and border element do not touch */
11805 else if (test_element == EL_PENGUIN)
11814 if (kill_x != -1 || kill_y != -1)
11816 if (IS_PLAYER(kill_x, kill_y))
11818 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11820 if (player->shield_deadly_time_left > 0 &&
11821 !IS_INDESTRUCTIBLE(bad_element))
11822 Bang(bad_x, bad_y);
11823 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11824 KillPlayer(player);
11827 Bang(kill_x, kill_y);
11831 void TestIfPlayerTouchesBadThing(int x, int y)
11833 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11836 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11838 TestIfGoodThingHitsBadThing(x, y, move_dir);
11841 void TestIfBadThingTouchesPlayer(int x, int y)
11843 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11846 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11848 TestIfBadThingHitsGoodThing(x, y, move_dir);
11851 void TestIfFriendTouchesBadThing(int x, int y)
11853 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11856 void TestIfBadThingTouchesFriend(int x, int y)
11858 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11861 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11863 int i, kill_x = bad_x, kill_y = bad_y;
11864 static int xy[4][2] =
11872 for (i = 0; i < NUM_DIRECTIONS; i++)
11876 x = bad_x + xy[i][0];
11877 y = bad_y + xy[i][1];
11878 if (!IN_LEV_FIELD(x, y))
11881 element = Feld[x][y];
11882 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11883 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11891 if (kill_x != bad_x || kill_y != bad_y)
11892 Bang(bad_x, bad_y);
11895 void KillPlayer(struct PlayerInfo *player)
11897 int jx = player->jx, jy = player->jy;
11899 if (!player->active)
11902 /* remove accessible field at the player's position */
11903 Feld[jx][jy] = EL_EMPTY;
11905 /* deactivate shield (else Bang()/Explode() would not work right) */
11906 player->shield_normal_time_left = 0;
11907 player->shield_deadly_time_left = 0;
11910 BuryPlayer(player);
11913 static void KillPlayerUnlessEnemyProtected(int x, int y)
11915 if (!PLAYER_ENEMY_PROTECTED(x, y))
11916 KillPlayer(PLAYERINFO(x, y));
11919 static void KillPlayerUnlessExplosionProtected(int x, int y)
11921 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11922 KillPlayer(PLAYERINFO(x, y));
11925 void BuryPlayer(struct PlayerInfo *player)
11927 int jx = player->jx, jy = player->jy;
11929 if (!player->active)
11932 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11933 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11935 player->GameOver = TRUE;
11936 RemovePlayer(player);
11939 void RemovePlayer(struct PlayerInfo *player)
11941 int jx = player->jx, jy = player->jy;
11942 int i, found = FALSE;
11944 player->present = FALSE;
11945 player->active = FALSE;
11947 if (!ExplodeField[jx][jy])
11948 StorePlayer[jx][jy] = 0;
11950 if (player->is_moving)
11951 DrawLevelField(player->last_jx, player->last_jy);
11953 for (i = 0; i < MAX_PLAYERS; i++)
11954 if (stored_player[i].active)
11958 AllPlayersGone = TRUE;
11964 #if USE_NEW_SNAP_DELAY
11965 static void setFieldForSnapping(int x, int y, int element, int direction)
11967 struct ElementInfo *ei = &element_info[element];
11968 int direction_bit = MV_DIR_TO_BIT(direction);
11969 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11970 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11971 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11973 Feld[x][y] = EL_ELEMENT_SNAPPING;
11974 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11976 ResetGfxAnimation(x, y);
11978 GfxElement[x][y] = element;
11979 GfxAction[x][y] = action;
11980 GfxDir[x][y] = direction;
11981 GfxFrame[x][y] = -1;
11986 =============================================================================
11987 checkDiagonalPushing()
11988 -----------------------------------------------------------------------------
11989 check if diagonal input device direction results in pushing of object
11990 (by checking if the alternative direction is walkable, diggable, ...)
11991 =============================================================================
11994 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11995 int x, int y, int real_dx, int real_dy)
11997 int jx, jy, dx, dy, xx, yy;
11999 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12002 /* diagonal direction: check alternative direction */
12007 xx = jx + (dx == 0 ? real_dx : 0);
12008 yy = jy + (dy == 0 ? real_dy : 0);
12010 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12014 =============================================================================
12016 -----------------------------------------------------------------------------
12017 x, y: field next to player (non-diagonal) to try to dig to
12018 real_dx, real_dy: direction as read from input device (can be diagonal)
12019 =============================================================================
12022 int DigField(struct PlayerInfo *player,
12023 int oldx, int oldy, int x, int y,
12024 int real_dx, int real_dy, int mode)
12026 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12027 boolean player_was_pushing = player->is_pushing;
12028 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12029 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12030 int jx = oldx, jy = oldy;
12031 int dx = x - jx, dy = y - jy;
12032 int nextx = x + dx, nexty = y + dy;
12033 int move_direction = (dx == -1 ? MV_LEFT :
12034 dx == +1 ? MV_RIGHT :
12036 dy == +1 ? MV_DOWN : MV_NONE);
12037 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12038 int dig_side = MV_DIR_OPPOSITE(move_direction);
12039 int old_element = Feld[jx][jy];
12040 #if USE_FIXED_DONT_RUN_INTO
12041 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12047 if (is_player) /* function can also be called by EL_PENGUIN */
12049 if (player->MovPos == 0)
12051 player->is_digging = FALSE;
12052 player->is_collecting = FALSE;
12055 if (player->MovPos == 0) /* last pushing move finished */
12056 player->is_pushing = FALSE;
12058 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12060 player->is_switching = FALSE;
12061 player->push_delay = -1;
12063 return MP_NO_ACTION;
12067 #if !USE_FIXED_DONT_RUN_INTO
12068 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12069 return MP_NO_ACTION;
12072 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12073 old_element = Back[jx][jy];
12075 /* in case of element dropped at player position, check background */
12076 else if (Back[jx][jy] != EL_EMPTY &&
12077 game.engine_version >= VERSION_IDENT(2,2,0,0))
12078 old_element = Back[jx][jy];
12080 /* checking here causes player to move into acid even if the current field
12081 cannot be left to that direction */
12083 #if USE_FIXED_DONT_RUN_INTO
12084 if (player_can_move && DONT_RUN_INTO(element))
12086 if (element == EL_ACID && dx == 0 && dy == 1)
12089 Feld[jx][jy] = EL_PLAYER_1;
12090 InitMovingField(jx, jy, MV_DOWN);
12091 Store[jx][jy] = EL_ACID;
12092 ContinueMoving(jx, jy);
12093 BuryPlayer(player);
12096 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12098 return MP_DONT_RUN_INTO;
12103 #if 1 /* ------------------------------ NEW ------------------------------ */
12105 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12106 return MP_NO_ACTION; /* field has no opening in this direction */
12108 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12109 return MP_NO_ACTION; /* field has no opening in this direction */
12111 #if USE_FIXED_DONT_RUN_INTO
12112 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12116 Feld[jx][jy] = player->artwork_element;
12118 Feld[jx][jy] = EL_PLAYER_1;
12120 InitMovingField(jx, jy, MV_DOWN);
12121 Store[jx][jy] = EL_ACID;
12122 ContinueMoving(jx, jy);
12123 BuryPlayer(player);
12125 return MP_DONT_RUN_INTO;
12129 #if USE_FIXED_DONT_RUN_INTO
12130 if (player_can_move && DONT_RUN_INTO(element))
12132 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12134 return MP_DONT_RUN_INTO;
12138 #else /* ------------------------------ OLD ------------------------------ */
12141 #if USE_FIXED_DONT_RUN_INTO
12142 if (player_can_move && DONT_RUN_INTO(element))
12144 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12146 return MP_DONT_RUN_INTO;
12151 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12152 return MP_NO_ACTION; /* field has no opening in this direction */
12154 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12155 return MP_NO_ACTION; /* field has no opening in this direction */
12157 /* checking here causes player to explode when moving into acid */
12159 #if USE_FIXED_DONT_RUN_INTO
12160 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12163 Feld[jx][jy] = EL_PLAYER_1;
12164 InitMovingField(jx, jy, MV_DOWN);
12165 Store[jx][jy] = EL_ACID;
12166 ContinueMoving(jx, jy);
12167 BuryPlayer(player);
12169 return MP_DONT_RUN_INTO;
12174 #endif /* ------------------------------ END ------------------------------ */
12177 #if USE_FIXED_DONT_RUN_INTO
12178 if (player_can_move && DONT_RUN_INTO(element))
12180 if (element == EL_ACID && dx == 0 && dy == 1)
12183 Feld[jx][jy] = EL_PLAYER_1;
12184 InitMovingField(jx, jy, MV_DOWN);
12185 Store[jx][jy] = EL_ACID;
12186 ContinueMoving(jx, jy);
12187 BuryPlayer(player);
12190 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12192 return MP_DONT_RUN_INTO;
12197 #if USE_FIXED_DONT_RUN_INTO
12198 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12199 return MP_NO_ACTION;
12202 #if !USE_FIXED_DONT_RUN_INTO
12203 element = Feld[x][y];
12206 collect_count = element_info[element].collect_count_initial;
12208 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12209 return MP_NO_ACTION;
12211 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12212 player_can_move = player_can_move_or_snap;
12214 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12215 game.engine_version >= VERSION_IDENT(2,2,0,0))
12217 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12218 player->index_bit, dig_side);
12219 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12220 player->index_bit, dig_side);
12222 if (Feld[x][y] != element) /* field changed by snapping */
12225 return MP_NO_ACTION;
12228 #if USE_PLAYER_GRAVITY
12229 if (player->gravity && is_player && !player->is_auto_moving &&
12230 canFallDown(player) && move_direction != MV_DOWN &&
12231 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12232 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12234 if (game.gravity && is_player && !player->is_auto_moving &&
12235 canFallDown(player) && move_direction != MV_DOWN &&
12236 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12237 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12240 if (player_can_move &&
12241 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12243 int sound_element = SND_ELEMENT(element);
12244 int sound_action = ACTION_WALKING;
12246 if (IS_RND_GATE(element))
12248 if (!player->key[RND_GATE_NR(element)])
12249 return MP_NO_ACTION;
12251 else if (IS_RND_GATE_GRAY(element))
12253 if (!player->key[RND_GATE_GRAY_NR(element)])
12254 return MP_NO_ACTION;
12256 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12258 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12259 return MP_NO_ACTION;
12261 else if (element == EL_EXIT_OPEN ||
12262 element == EL_SP_EXIT_OPEN ||
12263 element == EL_SP_EXIT_OPENING)
12265 sound_action = ACTION_PASSING; /* player is passing exit */
12267 else if (element == EL_EMPTY)
12269 sound_action = ACTION_MOVING; /* nothing to walk on */
12272 /* play sound from background or player, whatever is available */
12273 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12274 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12276 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12278 else if (player_can_move &&
12279 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12281 if (!ACCESS_FROM(element, opposite_direction))
12282 return MP_NO_ACTION; /* field not accessible from this direction */
12284 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12285 return MP_NO_ACTION;
12287 if (IS_EM_GATE(element))
12289 if (!player->key[EM_GATE_NR(element)])
12290 return MP_NO_ACTION;
12292 else if (IS_EM_GATE_GRAY(element))
12294 if (!player->key[EM_GATE_GRAY_NR(element)])
12295 return MP_NO_ACTION;
12297 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12299 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12300 return MP_NO_ACTION;
12302 else if (IS_EMC_GATE(element))
12304 if (!player->key[EMC_GATE_NR(element)])
12305 return MP_NO_ACTION;
12307 else if (IS_EMC_GATE_GRAY(element))
12309 if (!player->key[EMC_GATE_GRAY_NR(element)])
12310 return MP_NO_ACTION;
12312 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12314 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12315 return MP_NO_ACTION;
12317 else if (IS_SP_PORT(element))
12319 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12320 element == EL_SP_GRAVITY_PORT_RIGHT ||
12321 element == EL_SP_GRAVITY_PORT_UP ||
12322 element == EL_SP_GRAVITY_PORT_DOWN)
12323 #if USE_PLAYER_GRAVITY
12324 player->gravity = !player->gravity;
12326 game.gravity = !game.gravity;
12328 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12329 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12330 element == EL_SP_GRAVITY_ON_PORT_UP ||
12331 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12332 #if USE_PLAYER_GRAVITY
12333 player->gravity = TRUE;
12335 game.gravity = TRUE;
12337 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12338 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12339 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12340 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12341 #if USE_PLAYER_GRAVITY
12342 player->gravity = FALSE;
12344 game.gravity = FALSE;
12348 /* automatically move to the next field with double speed */
12349 player->programmed_action = move_direction;
12351 if (player->move_delay_reset_counter == 0)
12353 player->move_delay_reset_counter = 2; /* two double speed steps */
12355 DOUBLE_PLAYER_SPEED(player);
12358 PlayLevelSoundAction(x, y, ACTION_PASSING);
12360 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12364 if (mode != DF_SNAP)
12366 GfxElement[x][y] = GFX_ELEMENT(element);
12367 player->is_digging = TRUE;
12370 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12372 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12373 player->index_bit, dig_side);
12375 if (mode == DF_SNAP)
12377 #if USE_NEW_SNAP_DELAY
12378 if (level.block_snap_field)
12379 setFieldForSnapping(x, y, element, move_direction);
12381 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12383 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12386 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12387 player->index_bit, dig_side);
12390 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12394 if (is_player && mode != DF_SNAP)
12396 GfxElement[x][y] = element;
12397 player->is_collecting = TRUE;
12400 if (element == EL_SPEED_PILL)
12402 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12404 else if (element == EL_EXTRA_TIME && level.time > 0)
12406 TimeLeft += level.extra_time;
12407 DrawGameValue_Time(TimeLeft);
12409 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12411 player->shield_normal_time_left += level.shield_normal_time;
12412 if (element == EL_SHIELD_DEADLY)
12413 player->shield_deadly_time_left += level.shield_deadly_time;
12415 else if (element == EL_DYNAMITE ||
12416 element == EL_EM_DYNAMITE ||
12417 element == EL_SP_DISK_RED)
12419 if (player->inventory_size < MAX_INVENTORY_SIZE)
12420 player->inventory_element[player->inventory_size++] = element;
12423 DrawGameDoorValues();
12425 DrawGameValue_Dynamite(local_player->inventory_size);
12428 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12430 player->dynabomb_count++;
12431 player->dynabombs_left++;
12433 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12435 player->dynabomb_size++;
12437 else if (element == EL_DYNABOMB_INCREASE_POWER)
12439 player->dynabomb_xl = TRUE;
12441 else if (IS_KEY(element))
12443 player->key[KEY_NR(element)] = TRUE;
12446 DrawGameDoorValues();
12448 DrawGameValue_Keys(player->key);
12451 redraw_mask |= REDRAW_DOOR_1;
12453 else if (IS_ENVELOPE(element))
12455 player->show_envelope = element;
12457 else if (element == EL_EMC_LENSES)
12459 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12461 RedrawAllInvisibleElementsForLenses();
12463 else if (element == EL_EMC_MAGNIFIER)
12465 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12467 RedrawAllInvisibleElementsForMagnifier();
12469 else if (IS_DROPPABLE(element) ||
12470 IS_THROWABLE(element)) /* can be collected and dropped */
12474 if (collect_count == 0)
12475 player->inventory_infinite_element = element;
12477 for (i = 0; i < collect_count; i++)
12478 if (player->inventory_size < MAX_INVENTORY_SIZE)
12479 player->inventory_element[player->inventory_size++] = element;
12482 DrawGameDoorValues();
12484 DrawGameValue_Dynamite(local_player->inventory_size);
12487 else if (collect_count > 0)
12489 local_player->gems_still_needed -= collect_count;
12490 if (local_player->gems_still_needed < 0)
12491 local_player->gems_still_needed = 0;
12493 DrawGameValue_Emeralds(local_player->gems_still_needed);
12496 RaiseScoreElement(element);
12497 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12500 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12501 player->index_bit, dig_side);
12503 if (mode == DF_SNAP)
12505 #if USE_NEW_SNAP_DELAY
12506 if (level.block_snap_field)
12507 setFieldForSnapping(x, y, element, move_direction);
12509 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12511 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12514 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12515 player->index_bit, dig_side);
12518 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12520 if (mode == DF_SNAP && element != EL_BD_ROCK)
12521 return MP_NO_ACTION;
12523 if (CAN_FALL(element) && dy)
12524 return MP_NO_ACTION;
12526 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12527 !(element == EL_SPRING && level.use_spring_bug))
12528 return MP_NO_ACTION;
12530 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12531 ((move_direction & MV_VERTICAL &&
12532 ((element_info[element].move_pattern & MV_LEFT &&
12533 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12534 (element_info[element].move_pattern & MV_RIGHT &&
12535 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12536 (move_direction & MV_HORIZONTAL &&
12537 ((element_info[element].move_pattern & MV_UP &&
12538 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12539 (element_info[element].move_pattern & MV_DOWN &&
12540 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12541 return MP_NO_ACTION;
12543 /* do not push elements already moving away faster than player */
12544 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12545 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12546 return MP_NO_ACTION;
12548 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12550 if (player->push_delay_value == -1 || !player_was_pushing)
12551 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12553 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12555 if (player->push_delay_value == -1)
12556 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12558 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12560 if (!player->is_pushing)
12561 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12564 player->is_pushing = TRUE;
12565 player->is_active = TRUE;
12567 if (!(IN_LEV_FIELD(nextx, nexty) &&
12568 (IS_FREE(nextx, nexty) ||
12569 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12570 IS_SB_ELEMENT(element)))))
12571 return MP_NO_ACTION;
12573 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12574 return MP_NO_ACTION;
12576 if (player->push_delay == -1) /* new pushing; restart delay */
12577 player->push_delay = 0;
12579 if (player->push_delay < player->push_delay_value &&
12580 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12581 element != EL_SPRING && element != EL_BALLOON)
12583 /* make sure that there is no move delay before next try to push */
12584 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12585 player->move_delay = 0;
12587 return MP_NO_ACTION;
12590 if (IS_SB_ELEMENT(element))
12592 if (element == EL_SOKOBAN_FIELD_FULL)
12594 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12595 local_player->sokobanfields_still_needed++;
12598 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12600 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12601 local_player->sokobanfields_still_needed--;
12604 Feld[x][y] = EL_SOKOBAN_OBJECT;
12606 if (Back[x][y] == Back[nextx][nexty])
12607 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12608 else if (Back[x][y] != 0)
12609 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12612 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12615 if (local_player->sokobanfields_still_needed == 0 &&
12616 game.emulation == EMU_SOKOBAN)
12618 player->LevelSolved = player->GameOver = TRUE;
12619 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12623 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12625 InitMovingField(x, y, move_direction);
12626 GfxAction[x][y] = ACTION_PUSHING;
12628 if (mode == DF_SNAP)
12629 ContinueMoving(x, y);
12631 MovPos[x][y] = (dx != 0 ? dx : dy);
12633 Pushed[x][y] = TRUE;
12634 Pushed[nextx][nexty] = TRUE;
12636 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12637 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12639 player->push_delay_value = -1; /* get new value later */
12641 /* check for element change _after_ element has been pushed */
12642 if (game.use_change_when_pushing_bug)
12644 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12645 player->index_bit, dig_side);
12646 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12647 player->index_bit, dig_side);
12650 else if (IS_SWITCHABLE(element))
12652 if (PLAYER_SWITCHING(player, x, y))
12654 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12655 player->index_bit, dig_side);
12660 player->is_switching = TRUE;
12661 player->switch_x = x;
12662 player->switch_y = y;
12664 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12666 if (element == EL_ROBOT_WHEEL)
12668 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12672 DrawLevelField(x, y);
12674 else if (element == EL_SP_TERMINAL)
12679 SCAN_PLAYFIELD(xx, yy)
12681 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12684 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12686 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12687 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12690 else if (IS_BELT_SWITCH(element))
12692 ToggleBeltSwitch(x, y);
12694 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12695 element == EL_SWITCHGATE_SWITCH_DOWN)
12697 ToggleSwitchgateSwitch(x, y);
12699 else if (element == EL_LIGHT_SWITCH ||
12700 element == EL_LIGHT_SWITCH_ACTIVE)
12702 ToggleLightSwitch(x, y);
12704 else if (element == EL_TIMEGATE_SWITCH)
12706 ActivateTimegateSwitch(x, y);
12708 else if (element == EL_BALLOON_SWITCH_LEFT ||
12709 element == EL_BALLOON_SWITCH_RIGHT ||
12710 element == EL_BALLOON_SWITCH_UP ||
12711 element == EL_BALLOON_SWITCH_DOWN ||
12712 element == EL_BALLOON_SWITCH_NONE ||
12713 element == EL_BALLOON_SWITCH_ANY)
12715 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12716 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12717 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12718 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12719 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12722 else if (element == EL_LAMP)
12724 Feld[x][y] = EL_LAMP_ACTIVE;
12725 local_player->lights_still_needed--;
12727 ResetGfxAnimation(x, y);
12728 DrawLevelField(x, y);
12730 else if (element == EL_TIME_ORB_FULL)
12732 Feld[x][y] = EL_TIME_ORB_EMPTY;
12734 if (level.time > 0 || level.use_time_orb_bug)
12736 TimeLeft += level.time_orb_time;
12737 DrawGameValue_Time(TimeLeft);
12740 ResetGfxAnimation(x, y);
12741 DrawLevelField(x, y);
12743 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12744 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12748 game.ball_state = !game.ball_state;
12751 SCAN_PLAYFIELD(xx, yy)
12753 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12756 int e = Feld[xx][yy];
12758 if (game.ball_state)
12760 if (e == EL_EMC_MAGIC_BALL)
12761 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12762 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12763 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12767 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12768 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12769 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12770 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12775 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12776 player->index_bit, dig_side);
12778 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12779 player->index_bit, dig_side);
12781 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12782 player->index_bit, dig_side);
12788 if (!PLAYER_SWITCHING(player, x, y))
12790 player->is_switching = TRUE;
12791 player->switch_x = x;
12792 player->switch_y = y;
12794 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12795 player->index_bit, dig_side);
12796 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12797 player->index_bit, dig_side);
12799 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12800 player->index_bit, dig_side);
12801 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12802 player->index_bit, dig_side);
12805 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12806 player->index_bit, dig_side);
12807 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12808 player->index_bit, dig_side);
12810 return MP_NO_ACTION;
12813 player->push_delay = -1;
12815 if (is_player) /* function can also be called by EL_PENGUIN */
12817 if (Feld[x][y] != element) /* really digged/collected something */
12819 player->is_collecting = !player->is_digging;
12820 player->is_active = TRUE;
12827 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12829 int jx = player->jx, jy = player->jy;
12830 int x = jx + dx, y = jy + dy;
12831 int snap_direction = (dx == -1 ? MV_LEFT :
12832 dx == +1 ? MV_RIGHT :
12834 dy == +1 ? MV_DOWN : MV_NONE);
12835 boolean can_continue_snapping = (level.continuous_snapping &&
12836 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12838 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12841 if (!player->active || !IN_LEV_FIELD(x, y))
12849 if (player->MovPos == 0)
12850 player->is_pushing = FALSE;
12852 player->is_snapping = FALSE;
12854 if (player->MovPos == 0)
12856 player->is_moving = FALSE;
12857 player->is_digging = FALSE;
12858 player->is_collecting = FALSE;
12864 #if USE_NEW_CONTINUOUS_SNAPPING
12865 /* prevent snapping with already pressed snap key when not allowed */
12866 if (player->is_snapping && !can_continue_snapping)
12869 if (player->is_snapping)
12873 player->MovDir = snap_direction;
12875 if (player->MovPos == 0)
12877 player->is_moving = FALSE;
12878 player->is_digging = FALSE;
12879 player->is_collecting = FALSE;
12882 player->is_dropping = FALSE;
12883 player->is_dropping_pressed = FALSE;
12884 player->drop_pressed_delay = 0;
12886 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12889 player->is_snapping = TRUE;
12890 player->is_active = TRUE;
12892 if (player->MovPos == 0)
12894 player->is_moving = FALSE;
12895 player->is_digging = FALSE;
12896 player->is_collecting = FALSE;
12899 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12900 DrawLevelField(player->last_jx, player->last_jy);
12902 DrawLevelField(x, y);
12907 boolean DropElement(struct PlayerInfo *player)
12909 int old_element, new_element;
12910 int dropx = player->jx, dropy = player->jy;
12911 int drop_direction = player->MovDir;
12912 int drop_side = drop_direction;
12913 int drop_element = (player->inventory_size > 0 ?
12914 player->inventory_element[player->inventory_size - 1] :
12915 player->inventory_infinite_element != EL_UNDEFINED ?
12916 player->inventory_infinite_element :
12917 player->dynabombs_left > 0 ?
12918 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12921 player->is_dropping_pressed = TRUE;
12923 /* do not drop an element on top of another element; when holding drop key
12924 pressed without moving, dropped element must move away before the next
12925 element can be dropped (this is especially important if the next element
12926 is dynamite, which can be placed on background for historical reasons) */
12927 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12930 if (IS_THROWABLE(drop_element))
12932 dropx += GET_DX_FROM_DIR(drop_direction);
12933 dropy += GET_DY_FROM_DIR(drop_direction);
12935 if (!IN_LEV_FIELD(dropx, dropy))
12939 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12940 new_element = drop_element; /* default: no change when dropping */
12942 /* check if player is active, not moving and ready to drop */
12943 if (!player->active || player->MovPos || player->drop_delay > 0)
12946 /* check if player has anything that can be dropped */
12947 if (new_element == EL_UNDEFINED)
12950 /* check if drop key was pressed long enough for EM style dynamite */
12951 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12954 /* check if anything can be dropped at the current position */
12955 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12958 /* collected custom elements can only be dropped on empty fields */
12959 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12962 if (old_element != EL_EMPTY)
12963 Back[dropx][dropy] = old_element; /* store old element on this field */
12965 ResetGfxAnimation(dropx, dropy);
12966 ResetRandomAnimationValue(dropx, dropy);
12968 if (player->inventory_size > 0 ||
12969 player->inventory_infinite_element != EL_UNDEFINED)
12971 if (player->inventory_size > 0)
12973 player->inventory_size--;
12976 DrawGameDoorValues();
12978 DrawGameValue_Dynamite(local_player->inventory_size);
12981 if (new_element == EL_DYNAMITE)
12982 new_element = EL_DYNAMITE_ACTIVE;
12983 else if (new_element == EL_EM_DYNAMITE)
12984 new_element = EL_EM_DYNAMITE_ACTIVE;
12985 else if (new_element == EL_SP_DISK_RED)
12986 new_element = EL_SP_DISK_RED_ACTIVE;
12989 Feld[dropx][dropy] = new_element;
12991 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12992 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12993 el2img(Feld[dropx][dropy]), 0);
12995 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12997 /* needed if previous element just changed to "empty" in the last frame */
12998 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13000 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13001 player->index_bit, drop_side);
13002 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13004 player->index_bit, drop_side);
13006 TestIfElementTouchesCustomElement(dropx, dropy);
13008 else /* player is dropping a dyna bomb */
13010 player->dynabombs_left--;
13012 Feld[dropx][dropy] = new_element;
13014 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13015 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13016 el2img(Feld[dropx][dropy]), 0);
13018 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13021 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13022 InitField_WithBug1(dropx, dropy, FALSE);
13024 new_element = Feld[dropx][dropy]; /* element might have changed */
13026 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13027 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13029 int move_direction, nextx, nexty;
13031 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13032 MovDir[dropx][dropy] = drop_direction;
13034 move_direction = MovDir[dropx][dropy];
13035 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13036 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13038 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13039 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13042 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13043 player->is_dropping = TRUE;
13045 player->drop_pressed_delay = 0;
13046 player->is_dropping_pressed = FALSE;
13048 player->drop_x = dropx;
13049 player->drop_y = dropy;
13054 /* ------------------------------------------------------------------------- */
13055 /* game sound playing functions */
13056 /* ------------------------------------------------------------------------- */
13058 static int *loop_sound_frame = NULL;
13059 static int *loop_sound_volume = NULL;
13061 void InitPlayLevelSound()
13063 int num_sounds = getSoundListSize();
13065 checked_free(loop_sound_frame);
13066 checked_free(loop_sound_volume);
13068 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13069 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13072 static void PlayLevelSound(int x, int y, int nr)
13074 int sx = SCREENX(x), sy = SCREENY(y);
13075 int volume, stereo_position;
13076 int max_distance = 8;
13077 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13079 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13080 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13083 if (!IN_LEV_FIELD(x, y) ||
13084 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13085 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13088 volume = SOUND_MAX_VOLUME;
13090 if (!IN_SCR_FIELD(sx, sy))
13092 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13093 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13095 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13098 stereo_position = (SOUND_MAX_LEFT +
13099 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13100 (SCR_FIELDX + 2 * max_distance));
13102 if (IS_LOOP_SOUND(nr))
13104 /* This assures that quieter loop sounds do not overwrite louder ones,
13105 while restarting sound volume comparison with each new game frame. */
13107 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13110 loop_sound_volume[nr] = volume;
13111 loop_sound_frame[nr] = FrameCounter;
13114 PlaySoundExt(nr, volume, stereo_position, type);
13117 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13119 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13120 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13121 y < LEVELY(BY1) ? LEVELY(BY1) :
13122 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13126 static void PlayLevelSoundAction(int x, int y, int action)
13128 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13131 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13133 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13135 if (sound_effect != SND_UNDEFINED)
13136 PlayLevelSound(x, y, sound_effect);
13139 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13142 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13144 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13145 PlayLevelSound(x, y, sound_effect);
13148 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13150 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13152 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13153 PlayLevelSound(x, y, sound_effect);
13156 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13158 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13160 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13161 StopSound(sound_effect);
13164 static void PlayLevelMusic()
13166 if (levelset.music[level_nr] != MUS_UNDEFINED)
13167 PlayMusic(levelset.music[level_nr]); /* from config file */
13169 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13172 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
13174 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13179 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13183 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13187 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13191 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13195 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13199 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13203 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13206 case SAMPLE_android_clone:
13207 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13210 case SAMPLE_android_move:
13211 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13214 case SAMPLE_spring:
13215 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13219 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13223 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13226 case SAMPLE_eater_eat:
13227 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13231 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13234 case SAMPLE_collect:
13235 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13238 case SAMPLE_diamond:
13239 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13242 case SAMPLE_squash:
13243 /* !!! CHECK THIS !!! */
13245 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13247 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13251 case SAMPLE_wonderfall:
13252 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13256 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13260 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13264 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13268 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13272 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13276 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13279 case SAMPLE_wonder:
13280 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13284 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13287 case SAMPLE_exit_open:
13288 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13291 case SAMPLE_exit_leave:
13292 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13295 case SAMPLE_dynamite:
13296 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13300 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13304 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13308 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13312 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13316 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13320 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13324 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13329 void RaiseScore(int value)
13331 local_player->score += value;
13333 DrawGameValue_Score(local_player->score);
13336 void RaiseScoreElement(int element)
13341 case EL_BD_DIAMOND:
13342 case EL_EMERALD_YELLOW:
13343 case EL_EMERALD_RED:
13344 case EL_EMERALD_PURPLE:
13345 case EL_SP_INFOTRON:
13346 RaiseScore(level.score[SC_EMERALD]);
13349 RaiseScore(level.score[SC_DIAMOND]);
13352 RaiseScore(level.score[SC_CRYSTAL]);
13355 RaiseScore(level.score[SC_PEARL]);
13358 case EL_BD_BUTTERFLY:
13359 case EL_SP_ELECTRON:
13360 RaiseScore(level.score[SC_BUG]);
13363 case EL_BD_FIREFLY:
13364 case EL_SP_SNIKSNAK:
13365 RaiseScore(level.score[SC_SPACESHIP]);
13368 case EL_DARK_YAMYAM:
13369 RaiseScore(level.score[SC_YAMYAM]);
13372 RaiseScore(level.score[SC_ROBOT]);
13375 RaiseScore(level.score[SC_PACMAN]);
13378 RaiseScore(level.score[SC_NUT]);
13381 case EL_EM_DYNAMITE:
13382 case EL_SP_DISK_RED:
13383 case EL_DYNABOMB_INCREASE_NUMBER:
13384 case EL_DYNABOMB_INCREASE_SIZE:
13385 case EL_DYNABOMB_INCREASE_POWER:
13386 RaiseScore(level.score[SC_DYNAMITE]);
13388 case EL_SHIELD_NORMAL:
13389 case EL_SHIELD_DEADLY:
13390 RaiseScore(level.score[SC_SHIELD]);
13392 case EL_EXTRA_TIME:
13393 RaiseScore(level.extra_time_score);
13407 RaiseScore(level.score[SC_KEY]);
13410 RaiseScore(element_info[element].collect_score);
13415 void RequestQuitGame(boolean ask_if_really_quit)
13417 if (AllPlayersGone ||
13418 !ask_if_really_quit ||
13419 level_editor_test_game ||
13420 Request("Do you really want to quit the game ?",
13421 REQ_ASK | REQ_STAY_CLOSED))
13423 #if defined(NETWORK_AVALIABLE)
13424 if (options.network)
13425 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13429 if (!ask_if_really_quit || level_editor_test_game)
13431 game_status = GAME_MODE_MAIN;
13437 FadeOutField(TITLE_SCREEN_FADE_DELAY, TITLE_SCREEN_POST_DELAY);
13439 game_status = GAME_MODE_MAIN;
13441 DrawMainMenuExt(TITLE_SCREEN_FADE_DELAY, REDRAW_FIELD);
13447 if (tape.playing && tape.deactivate_display)
13448 TapeDeactivateDisplayOff(TRUE);
13450 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13452 if (tape.playing && tape.deactivate_display)
13453 TapeDeactivateDisplayOn();
13458 /* ---------- new game button stuff ---------------------------------------- */
13460 /* graphic position values for game buttons */
13461 #define GAME_BUTTON_XSIZE 30
13462 #define GAME_BUTTON_YSIZE 30
13463 #define GAME_BUTTON_XPOS 5
13464 #define GAME_BUTTON_YPOS 215
13465 #define SOUND_BUTTON_XPOS 5
13466 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13468 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13469 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13470 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13471 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13472 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13473 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13480 } gamebutton_info[NUM_GAME_BUTTONS] =
13483 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13488 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13489 GAME_CTRL_ID_PAUSE,
13493 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13498 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13499 SOUND_CTRL_ID_MUSIC,
13500 "background music on/off"
13503 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13504 SOUND_CTRL_ID_LOOPS,
13505 "sound loops on/off"
13508 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13509 SOUND_CTRL_ID_SIMPLE,
13510 "normal sounds on/off"
13514 void CreateGameButtons()
13518 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13520 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13521 struct GadgetInfo *gi;
13524 unsigned long event_mask;
13525 int gd_xoffset, gd_yoffset;
13526 int gd_x1, gd_x2, gd_y1, gd_y2;
13529 gd_xoffset = gamebutton_info[i].x;
13530 gd_yoffset = gamebutton_info[i].y;
13531 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13532 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13534 if (id == GAME_CTRL_ID_STOP ||
13535 id == GAME_CTRL_ID_PAUSE ||
13536 id == GAME_CTRL_ID_PLAY)
13538 button_type = GD_TYPE_NORMAL_BUTTON;
13540 event_mask = GD_EVENT_RELEASED;
13541 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13542 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13546 button_type = GD_TYPE_CHECK_BUTTON;
13548 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13549 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13550 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13551 event_mask = GD_EVENT_PRESSED;
13552 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13553 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13556 gi = CreateGadget(GDI_CUSTOM_ID, id,
13557 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13558 GDI_X, DX + gd_xoffset,
13559 GDI_Y, DY + gd_yoffset,
13560 GDI_WIDTH, GAME_BUTTON_XSIZE,
13561 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13562 GDI_TYPE, button_type,
13563 GDI_STATE, GD_BUTTON_UNPRESSED,
13564 GDI_CHECKED, checked,
13565 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13566 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13567 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13568 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13569 GDI_EVENT_MASK, event_mask,
13570 GDI_CALLBACK_ACTION, HandleGameButtons,
13574 Error(ERR_EXIT, "cannot create gadget");
13576 game_gadget[id] = gi;
13580 void FreeGameButtons()
13584 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13585 FreeGadget(game_gadget[i]);
13588 static void MapGameButtons()
13592 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13593 MapGadget(game_gadget[i]);
13596 void UnmapGameButtons()
13600 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13601 UnmapGadget(game_gadget[i]);
13604 static void HandleGameButtons(struct GadgetInfo *gi)
13606 int id = gi->custom_id;
13608 if (game_status != GAME_MODE_PLAYING)
13613 case GAME_CTRL_ID_STOP:
13617 RequestQuitGame(TRUE);
13620 case GAME_CTRL_ID_PAUSE:
13621 if (options.network)
13623 #if defined(NETWORK_AVALIABLE)
13625 SendToServer_ContinuePlaying();
13627 SendToServer_PausePlaying();
13631 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13634 case GAME_CTRL_ID_PLAY:
13637 #if defined(NETWORK_AVALIABLE)
13638 if (options.network)
13639 SendToServer_ContinuePlaying();
13643 tape.pausing = FALSE;
13644 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13649 case SOUND_CTRL_ID_MUSIC:
13650 if (setup.sound_music)
13652 setup.sound_music = FALSE;
13655 else if (audio.music_available)
13657 setup.sound = setup.sound_music = TRUE;
13659 SetAudioMode(setup.sound);
13665 case SOUND_CTRL_ID_LOOPS:
13666 if (setup.sound_loops)
13667 setup.sound_loops = FALSE;
13668 else if (audio.loops_available)
13670 setup.sound = setup.sound_loops = TRUE;
13671 SetAudioMode(setup.sound);
13675 case SOUND_CTRL_ID_SIMPLE:
13676 if (setup.sound_simple)
13677 setup.sound_simple = FALSE;
13678 else if (audio.sound_available)
13680 setup.sound = setup.sound_simple = TRUE;
13681 SetAudioMode(setup.sound);