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);
2637 if (!game.restart_level)
2638 CloseDoor(DOOR_CLOSE_1);
2641 FadeOut(REDRAW_FIELD);
2643 /* !!! FIX THIS (START) !!! */
2644 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2646 InitGameEngine_EM();
2649 /* blit playfield from scroll buffer to normal back buffer for fading in */
2650 BlitScreenToBitmap_EM(backbuffer);
2658 /* after drawing the level, correct some elements */
2659 if (game.timegate_time_left == 0)
2660 CloseAllOpenTimegates();
2662 /* blit playfield from scroll buffer to normal back buffer for fading in */
2663 if (setup.soft_scrolling)
2664 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2666 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2672 /* !!! FIX THIS (END) !!! */
2675 FadeIn(REDRAW_FIELD);
2679 if (!game.restart_level)
2681 /* copy default game door content to main double buffer */
2682 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2683 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2687 SetPanelBackground();
2688 SetDrawBackgroundMask(REDRAW_DOOR_1);
2691 DrawGameDoorValues();
2693 if (!game.restart_level)
2697 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2698 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2699 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2703 /* copy actual game door content to door double buffer for OpenDoor() */
2704 BlitBitmap(drawto, bitmap_db_door,
2705 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2707 OpenDoor(DOOR_OPEN_ALL);
2709 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2711 if (setup.sound_music)
2714 KeyboardAutoRepeatOffUnlessAutoplay();
2718 for (i = 0; i < MAX_PLAYERS; i++)
2719 printf("Player %d %sactive.\n",
2720 i + 1, (stored_player[i].active ? "" : "not "));
2724 game.restart_level = FALSE;
2727 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2729 /* this is used for non-R'n'D game engines to update certain engine values */
2731 /* needed to determine if sounds are played within the visible screen area */
2732 scroll_x = actual_scroll_x;
2733 scroll_y = actual_scroll_y;
2736 void InitMovDir(int x, int y)
2738 int i, element = Feld[x][y];
2739 static int xy[4][2] =
2746 static int direction[3][4] =
2748 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2749 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2750 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2759 Feld[x][y] = EL_BUG;
2760 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2763 case EL_SPACESHIP_RIGHT:
2764 case EL_SPACESHIP_UP:
2765 case EL_SPACESHIP_LEFT:
2766 case EL_SPACESHIP_DOWN:
2767 Feld[x][y] = EL_SPACESHIP;
2768 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2771 case EL_BD_BUTTERFLY_RIGHT:
2772 case EL_BD_BUTTERFLY_UP:
2773 case EL_BD_BUTTERFLY_LEFT:
2774 case EL_BD_BUTTERFLY_DOWN:
2775 Feld[x][y] = EL_BD_BUTTERFLY;
2776 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2779 case EL_BD_FIREFLY_RIGHT:
2780 case EL_BD_FIREFLY_UP:
2781 case EL_BD_FIREFLY_LEFT:
2782 case EL_BD_FIREFLY_DOWN:
2783 Feld[x][y] = EL_BD_FIREFLY;
2784 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2787 case EL_PACMAN_RIGHT:
2789 case EL_PACMAN_LEFT:
2790 case EL_PACMAN_DOWN:
2791 Feld[x][y] = EL_PACMAN;
2792 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2795 case EL_YAMYAM_LEFT:
2796 case EL_YAMYAM_RIGHT:
2798 case EL_YAMYAM_DOWN:
2799 Feld[x][y] = EL_YAMYAM;
2800 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2803 case EL_SP_SNIKSNAK:
2804 MovDir[x][y] = MV_UP;
2807 case EL_SP_ELECTRON:
2808 MovDir[x][y] = MV_LEFT;
2815 Feld[x][y] = EL_MOLE;
2816 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2820 if (IS_CUSTOM_ELEMENT(element))
2822 struct ElementInfo *ei = &element_info[element];
2823 int move_direction_initial = ei->move_direction_initial;
2824 int move_pattern = ei->move_pattern;
2826 if (move_direction_initial == MV_START_PREVIOUS)
2828 if (MovDir[x][y] != MV_NONE)
2831 move_direction_initial = MV_START_AUTOMATIC;
2834 if (move_direction_initial == MV_START_RANDOM)
2835 MovDir[x][y] = 1 << RND(4);
2836 else if (move_direction_initial & MV_ANY_DIRECTION)
2837 MovDir[x][y] = move_direction_initial;
2838 else if (move_pattern == MV_ALL_DIRECTIONS ||
2839 move_pattern == MV_TURNING_LEFT ||
2840 move_pattern == MV_TURNING_RIGHT ||
2841 move_pattern == MV_TURNING_LEFT_RIGHT ||
2842 move_pattern == MV_TURNING_RIGHT_LEFT ||
2843 move_pattern == MV_TURNING_RANDOM)
2844 MovDir[x][y] = 1 << RND(4);
2845 else if (move_pattern == MV_HORIZONTAL)
2846 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2847 else if (move_pattern == MV_VERTICAL)
2848 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2849 else if (move_pattern & MV_ANY_DIRECTION)
2850 MovDir[x][y] = element_info[element].move_pattern;
2851 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2852 move_pattern == MV_ALONG_RIGHT_SIDE)
2854 /* use random direction as default start direction */
2855 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2856 MovDir[x][y] = 1 << RND(4);
2858 for (i = 0; i < NUM_DIRECTIONS; i++)
2860 int x1 = x + xy[i][0];
2861 int y1 = y + xy[i][1];
2863 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2865 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2866 MovDir[x][y] = direction[0][i];
2868 MovDir[x][y] = direction[1][i];
2877 MovDir[x][y] = 1 << RND(4);
2879 if (element != EL_BUG &&
2880 element != EL_SPACESHIP &&
2881 element != EL_BD_BUTTERFLY &&
2882 element != EL_BD_FIREFLY)
2885 for (i = 0; i < NUM_DIRECTIONS; i++)
2887 int x1 = x + xy[i][0];
2888 int y1 = y + xy[i][1];
2890 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2892 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2894 MovDir[x][y] = direction[0][i];
2897 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2898 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2900 MovDir[x][y] = direction[1][i];
2909 GfxDir[x][y] = MovDir[x][y];
2912 void InitAmoebaNr(int x, int y)
2915 int group_nr = AmoebeNachbarNr(x, y);
2919 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2921 if (AmoebaCnt[i] == 0)
2929 AmoebaNr[x][y] = group_nr;
2930 AmoebaCnt[group_nr]++;
2931 AmoebaCnt2[group_nr]++;
2934 #if USE_NEW_GAME_WON
2938 static boolean score_done = FALSE;
2939 static boolean player_done = FALSE;
2940 static int game_over_delay = 0;
2941 int game_over_delay_value = 50;
2943 /* do not start end game actions before the player stops moving (to exit) */
2944 if (local_player->MovPos)
2947 if (tape.auto_play) /* tape might already be stopped here */
2948 tape.auto_play_level_solved = TRUE;
2950 if (!local_player->LevelSolved_GameEnd)
2952 local_player->LevelSolved_GameEnd = TRUE;
2953 local_player->LevelSolved_SaveTape = tape.recording;
2954 local_player->LevelSolved_SaveScore = !tape.playing;
2957 player_done = FALSE;
2958 game_over_delay = 0;
2961 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2967 if (setup.sound_loops)
2968 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2969 SND_CTRL_PLAY_LOOP);
2971 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2974 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2977 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2982 RaiseScore(level.score[SC_TIME_BONUS]);
2985 DrawGameValue_Time(TimeLeft);
2992 if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
2993 StopSound(SND_GAME_LEVELTIME_BONUS);
2995 else if (level.time == 0 && TimePlayed < 999) /* level without time limit */
2999 if (setup.sound_loops)
3000 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3001 SND_CTRL_PLAY_LOOP);
3003 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3006 if (TimePlayed < 900 && TimePlayed % 10 == 0)
3009 RaiseScore(level.score[SC_TIME_BONUS] * 10);
3014 RaiseScore(level.score[SC_TIME_BONUS]);
3017 DrawGameValue_Time(TimePlayed);
3019 if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
3020 StopSound(SND_GAME_LEVELTIME_BONUS);
3027 /* close exit door after last player */
3028 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3029 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3030 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3032 int element = Feld[ExitX][ExitY];
3034 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3035 EL_SP_EXIT_CLOSING);
3037 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3040 /* player disappears */
3041 if (ExitX >= 0 && ExitY >= 0 && !player_done)
3043 DrawLevelField(ExitX, ExitY);
3050 if (game_over_delay < game_over_delay_value || !score_done)
3057 boolean raise_level = FALSE;
3059 CloseDoor(DOOR_CLOSE_1);
3061 if (local_player->LevelSolved_SaveTape)
3065 SaveTape(tape.level_nr); /* Ask to save tape */
3068 if (!local_player->LevelSolved_SaveScore)
3070 FadeOut(REDRAW_FIELD);
3072 game_status = GAME_MODE_MAIN;
3074 DrawAndFadeInMainMenu(REDRAW_FIELD);
3079 if (level_nr == leveldir_current->handicap_level)
3081 leveldir_current->handicap_level++;
3082 SaveLevelSetup_SeriesInfo();
3085 if (level_editor_test_game)
3086 local_player->score = -1; /* no highscore when playing from editor */
3087 else if (level_nr < leveldir_current->last_level)
3088 raise_level = TRUE; /* advance to next level */
3090 if ((hi_pos = NewHiScore()) >= 0)
3092 game_status = GAME_MODE_SCORES;
3094 DrawHallOfFame(hi_pos);
3104 FadeOut(REDRAW_FIELD);
3106 game_status = GAME_MODE_MAIN;
3114 DrawAndFadeInMainMenu(REDRAW_FIELD);
3117 local_player->LevelSolved_SaveScore = FALSE;
3125 boolean raise_level = FALSE;
3127 if (local_player->MovPos)
3130 if (tape.auto_play) /* tape might already be stopped here */
3131 tape.auto_play_level_solved = TRUE;
3133 local_player->LevelSolved = FALSE;
3135 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
3139 if (!tape.playing && setup.sound_loops)
3140 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3141 SND_CTRL_PLAY_LOOP);
3143 while (TimeLeft > 0)
3145 if (!tape.playing && !setup.sound_loops)
3146 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3148 if (TimeLeft > 100 && TimeLeft % 10 == 0)
3151 RaiseScore(level.score[SC_TIME_BONUS] * 10);
3156 RaiseScore(level.score[SC_TIME_BONUS]);
3159 DrawGameValue_Time(TimeLeft);
3167 if (!tape.playing && setup.sound_loops)
3168 StopSound(SND_GAME_LEVELTIME_BONUS);
3170 else if (level.time == 0) /* level without time limit */
3172 if (!tape.playing && setup.sound_loops)
3173 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3174 SND_CTRL_PLAY_LOOP);
3176 while (TimePlayed < 999)
3178 if (!tape.playing && !setup.sound_loops)
3179 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3181 if (TimePlayed < 900 && TimePlayed % 10 == 0)
3184 RaiseScore(level.score[SC_TIME_BONUS] * 10);
3189 RaiseScore(level.score[SC_TIME_BONUS]);
3192 DrawGameValue_Time(TimePlayed);
3200 if (!tape.playing && setup.sound_loops)
3201 StopSound(SND_GAME_LEVELTIME_BONUS);
3204 /* close exit door after last player */
3205 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3206 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3207 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3209 int element = Feld[ExitX][ExitY];
3211 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3212 EL_SP_EXIT_CLOSING);
3214 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3217 /* player disappears */
3218 if (ExitX >= 0 && ExitY >= 0)
3219 DrawLevelField(ExitX, ExitY);
3225 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
3227 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
3233 CloseDoor(DOOR_CLOSE_1);
3238 SaveTape(tape.level_nr); /* Ask to save tape */
3241 if (level_nr == leveldir_current->handicap_level)
3243 leveldir_current->handicap_level++;
3244 SaveLevelSetup_SeriesInfo();
3247 if (level_editor_test_game)
3248 local_player->score = -1; /* no highscore when playing from editor */
3249 else if (level_nr < leveldir_current->last_level)
3250 raise_level = TRUE; /* advance to next level */
3252 if ((hi_pos = NewHiScore()) >= 0)
3254 game_status = GAME_MODE_SCORES;
3255 DrawHallOfFame(hi_pos);
3264 game_status = GAME_MODE_MAIN;
3283 LoadScore(level_nr);
3285 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3286 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3289 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3291 if (local_player->score > highscore[k].Score)
3293 /* player has made it to the hall of fame */
3295 if (k < MAX_SCORE_ENTRIES - 1)
3297 int m = MAX_SCORE_ENTRIES - 1;
3300 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3301 if (strEqual(setup.player_name, highscore[l].Name))
3303 if (m == k) /* player's new highscore overwrites his old one */
3307 for (l = m; l > k; l--)
3309 strcpy(highscore[l].Name, highscore[l - 1].Name);
3310 highscore[l].Score = highscore[l - 1].Score;
3317 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3318 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3319 highscore[k].Score = local_player->score;
3325 else if (!strncmp(setup.player_name, highscore[k].Name,
3326 MAX_PLAYER_NAME_LEN))
3327 break; /* player already there with a higher score */
3333 SaveScore(level_nr);
3338 inline static int getElementMoveStepsize(int x, int y)
3340 int element = Feld[x][y];
3341 int direction = MovDir[x][y];
3342 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3343 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3344 int horiz_move = (dx != 0);
3345 int sign = (horiz_move ? dx : dy);
3346 int step = sign * element_info[element].move_stepsize;
3348 /* special values for move stepsize for spring and things on conveyor belt */
3352 if (element == EL_SPRING)
3353 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3354 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3355 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3356 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3358 if (CAN_FALL(element) &&
3359 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3360 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3361 else if (element == EL_SPRING)
3362 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3369 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3371 if (player->GfxAction != action || player->GfxDir != dir)
3374 printf("Player frame reset! (%d => %d, %d => %d)\n",
3375 player->GfxAction, action, player->GfxDir, dir);
3378 player->GfxAction = action;
3379 player->GfxDir = dir;
3381 player->StepFrame = 0;
3385 #if USE_GFX_RESET_GFX_ANIMATION
3386 static void ResetGfxFrame(int x, int y, boolean redraw)
3388 int element = Feld[x][y];
3389 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3390 int last_gfx_frame = GfxFrame[x][y];
3392 if (graphic_info[graphic].anim_global_sync)
3393 GfxFrame[x][y] = FrameCounter;
3394 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3395 GfxFrame[x][y] = CustomValue[x][y];
3396 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3397 GfxFrame[x][y] = element_info[element].collect_score;
3398 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3399 GfxFrame[x][y] = ChangeDelay[x][y];
3401 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3402 DrawLevelGraphicAnimation(x, y, graphic);
3406 static void ResetGfxAnimation(int x, int y)
3409 int element, graphic;
3412 GfxAction[x][y] = ACTION_DEFAULT;
3413 GfxDir[x][y] = MovDir[x][y];
3417 element = Feld[x][y];
3418 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3420 if (graphic_info[graphic].anim_global_sync)
3421 GfxFrame[x][y] = FrameCounter;
3422 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3423 GfxFrame[x][y] = CustomValue[x][y];
3424 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3425 GfxFrame[x][y] = element_info[element].collect_score;
3426 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3427 GfxFrame[x][y] = ChangeDelay[x][y];
3430 #if USE_GFX_RESET_GFX_ANIMATION
3431 ResetGfxFrame(x, y, FALSE);
3435 static void ResetRandomAnimationValue(int x, int y)
3437 GfxRandom[x][y] = INIT_GFX_RANDOM();
3440 void InitMovingField(int x, int y, int direction)
3442 int element = Feld[x][y];
3446 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3447 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3451 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3452 ResetGfxAnimation(x, y);
3454 MovDir[x][y] = direction;
3455 GfxDir[x][y] = direction;
3456 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3457 ACTION_FALLING : ACTION_MOVING);
3460 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3462 if (graphic_info[graphic].anim_global_sync)
3463 GfxFrame[x][y] = FrameCounter;
3464 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3465 GfxFrame[x][y] = CustomValue[x][y];
3466 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3467 GfxFrame[x][y] = element_info[element].collect_score;
3468 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3469 GfxFrame[x][y] = ChangeDelay[x][y];
3472 /* this is needed for CEs with property "can move" / "not moving" */
3474 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3476 if (Feld[newx][newy] == EL_EMPTY)
3477 Feld[newx][newy] = EL_BLOCKED;
3479 MovDir[newx][newy] = MovDir[x][y];
3481 #if USE_NEW_CUSTOM_VALUE
3482 CustomValue[newx][newy] = CustomValue[x][y];
3485 GfxFrame[newx][newy] = GfxFrame[x][y];
3486 GfxRandom[newx][newy] = GfxRandom[x][y];
3487 GfxAction[newx][newy] = GfxAction[x][y];
3488 GfxDir[newx][newy] = GfxDir[x][y];
3492 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3494 int direction = MovDir[x][y];
3496 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3497 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3499 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3500 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3507 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3509 int oldx = x, oldy = y;
3510 int direction = MovDir[x][y];
3512 if (direction == MV_LEFT)
3514 else if (direction == MV_RIGHT)
3516 else if (direction == MV_UP)
3518 else if (direction == MV_DOWN)
3521 *comes_from_x = oldx;
3522 *comes_from_y = oldy;
3525 int MovingOrBlocked2Element(int x, int y)
3527 int element = Feld[x][y];
3529 if (element == EL_BLOCKED)
3533 Blocked2Moving(x, y, &oldx, &oldy);
3534 return Feld[oldx][oldy];
3540 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3542 /* like MovingOrBlocked2Element(), but if element is moving
3543 and (x,y) is the field the moving element is just leaving,
3544 return EL_BLOCKED instead of the element value */
3545 int element = Feld[x][y];
3547 if (IS_MOVING(x, y))
3549 if (element == EL_BLOCKED)
3553 Blocked2Moving(x, y, &oldx, &oldy);
3554 return Feld[oldx][oldy];
3563 static void RemoveField(int x, int y)
3565 Feld[x][y] = EL_EMPTY;
3571 #if USE_NEW_CUSTOM_VALUE
3572 CustomValue[x][y] = 0;
3576 ChangeDelay[x][y] = 0;
3577 ChangePage[x][y] = -1;
3578 Pushed[x][y] = FALSE;
3581 ExplodeField[x][y] = EX_TYPE_NONE;
3584 GfxElement[x][y] = EL_UNDEFINED;
3585 GfxAction[x][y] = ACTION_DEFAULT;
3586 GfxDir[x][y] = MV_NONE;
3589 void RemoveMovingField(int x, int y)
3591 int oldx = x, oldy = y, newx = x, newy = y;
3592 int element = Feld[x][y];
3593 int next_element = EL_UNDEFINED;
3595 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3598 if (IS_MOVING(x, y))
3600 Moving2Blocked(x, y, &newx, &newy);
3602 if (Feld[newx][newy] != EL_BLOCKED)
3604 /* element is moving, but target field is not free (blocked), but
3605 already occupied by something different (example: acid pool);
3606 in this case, only remove the moving field, but not the target */
3608 RemoveField(oldx, oldy);
3610 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3612 DrawLevelField(oldx, oldy);
3617 else if (element == EL_BLOCKED)
3619 Blocked2Moving(x, y, &oldx, &oldy);
3620 if (!IS_MOVING(oldx, oldy))
3624 if (element == EL_BLOCKED &&
3625 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3626 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3627 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3628 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3629 next_element = get_next_element(Feld[oldx][oldy]);
3631 RemoveField(oldx, oldy);
3632 RemoveField(newx, newy);
3634 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3636 if (next_element != EL_UNDEFINED)
3637 Feld[oldx][oldy] = next_element;
3639 DrawLevelField(oldx, oldy);
3640 DrawLevelField(newx, newy);
3643 void DrawDynamite(int x, int y)
3645 int sx = SCREENX(x), sy = SCREENY(y);
3646 int graphic = el2img(Feld[x][y]);
3649 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3652 if (IS_WALKABLE_INSIDE(Back[x][y]))
3656 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3657 else if (Store[x][y])
3658 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3660 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3662 if (Back[x][y] || Store[x][y])
3663 DrawGraphicThruMask(sx, sy, graphic, frame);
3665 DrawGraphic(sx, sy, graphic, frame);
3668 void CheckDynamite(int x, int y)
3670 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3674 if (MovDelay[x][y] != 0)
3677 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3683 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3690 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3692 boolean num_checked_players = 0;
3695 for (i = 0; i < MAX_PLAYERS; i++)
3697 if (stored_player[i].active)
3699 int sx = stored_player[i].jx;
3700 int sy = stored_player[i].jy;
3702 if (num_checked_players == 0)
3709 *sx1 = MIN(*sx1, sx);
3710 *sy1 = MIN(*sy1, sy);
3711 *sx2 = MAX(*sx2, sx);
3712 *sy2 = MAX(*sy2, sy);
3715 num_checked_players++;
3720 static boolean checkIfAllPlayersFitToScreen_RND()
3722 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3724 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3726 return (sx2 - sx1 < SCR_FIELDX &&
3727 sy2 - sy1 < SCR_FIELDY);
3730 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3732 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3734 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3736 *sx = (sx1 + sx2) / 2;
3737 *sy = (sy1 + sy2) / 2;
3741 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3742 int center_x, int center_y)
3744 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3746 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3748 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3749 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3752 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3756 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3758 return (max_dx <= SCR_FIELDX / 2 &&
3759 max_dy <= SCR_FIELDY / 2);
3767 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3768 boolean quick_relocation)
3770 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3771 boolean no_delay = (tape.warp_forward);
3772 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3773 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3775 if (quick_relocation)
3777 int offset = (setup.scroll_delay ? 3 : 0);
3784 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3786 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3787 x > SBX_Right + MIDPOSX ? SBX_Right :
3790 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3791 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3796 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3797 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3798 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3800 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3801 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3802 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3804 /* don't scroll over playfield boundaries */
3805 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3806 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3808 /* don't scroll over playfield boundaries */
3809 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3810 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3813 RedrawPlayfield(TRUE, 0,0,0,0);
3817 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3818 x > SBX_Right + MIDPOSX ? SBX_Right :
3821 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3822 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3825 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3827 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3830 int fx = FX, fy = FY;
3832 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3833 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3835 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3841 fx += dx * TILEX / 2;
3842 fy += dy * TILEY / 2;
3844 ScrollLevel(dx, dy);
3847 /* scroll in two steps of half tile size to make things smoother */
3848 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3850 Delay(wait_delay_value);
3852 /* scroll second step to align at full tile size */
3854 Delay(wait_delay_value);
3859 Delay(wait_delay_value);
3865 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3867 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3868 boolean no_delay = (tape.warp_forward);
3869 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3870 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3871 int jx = player->jx;
3872 int jy = player->jy;
3874 if (quick_relocation)
3876 int offset = (setup.scroll_delay ? 3 : 0);
3878 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3880 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3881 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3882 player->jx - MIDPOSX);
3884 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3885 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3886 player->jy - MIDPOSY);
3890 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3891 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3892 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3894 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3895 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3896 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3898 /* don't scroll over playfield boundaries */
3899 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3900 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3902 /* don't scroll over playfield boundaries */
3903 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3904 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3907 RedrawPlayfield(TRUE, 0,0,0,0);
3911 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3912 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3913 player->jx - MIDPOSX);
3915 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3916 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3917 player->jy - MIDPOSY);
3919 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3921 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3924 int fx = FX, fy = FY;
3926 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3927 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3929 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3935 fx += dx * TILEX / 2;
3936 fy += dy * TILEY / 2;
3938 ScrollLevel(dx, dy);
3941 /* scroll in two steps of half tile size to make things smoother */
3942 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3944 Delay(wait_delay_value);
3946 /* scroll second step to align at full tile size */
3948 Delay(wait_delay_value);
3953 Delay(wait_delay_value);
3959 void RelocatePlayer(int jx, int jy, int el_player_raw)
3961 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3962 int player_nr = GET_PLAYER_NR(el_player);
3963 struct PlayerInfo *player = &stored_player[player_nr];
3964 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3965 boolean no_delay = (tape.warp_forward);
3966 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3967 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3968 int old_jx = player->jx;
3969 int old_jy = player->jy;
3970 int old_element = Feld[old_jx][old_jy];
3971 int element = Feld[jx][jy];
3972 boolean player_relocated = (old_jx != jx || old_jy != jy);
3974 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3975 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3976 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3977 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3978 int leave_side_horiz = move_dir_horiz;
3979 int leave_side_vert = move_dir_vert;
3980 int enter_side = enter_side_horiz | enter_side_vert;
3981 int leave_side = leave_side_horiz | leave_side_vert;
3983 if (player->GameOver) /* do not reanimate dead player */
3986 if (!player_relocated) /* no need to relocate the player */
3989 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3991 RemoveField(jx, jy); /* temporarily remove newly placed player */
3992 DrawLevelField(jx, jy);
3995 if (player->present)
3997 while (player->MovPos)
3999 ScrollPlayer(player, SCROLL_GO_ON);
4000 ScrollScreen(NULL, SCROLL_GO_ON);
4002 AdvanceFrameAndPlayerCounters(player->index_nr);
4007 Delay(wait_delay_value);
4010 DrawPlayer(player); /* needed here only to cleanup last field */
4011 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4013 player->is_moving = FALSE;
4016 if (IS_CUSTOM_ELEMENT(old_element))
4017 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4019 player->index_bit, leave_side);
4021 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4023 player->index_bit, leave_side);
4025 Feld[jx][jy] = el_player;
4026 InitPlayerField(jx, jy, el_player, TRUE);
4028 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4030 Feld[jx][jy] = element;
4031 InitField(jx, jy, FALSE);
4035 /* only visually relocate centered player */
4037 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
4038 level.instant_relocation);
4040 if (player->index_nr == game.centered_player_nr)
4041 DrawRelocatePlayer(player, level.instant_relocation);
4044 if (player == local_player) /* only visually relocate local player */
4045 DrawRelocatePlayer(player, level.instant_relocation);
4048 TestIfPlayerTouchesBadThing(jx, jy);
4049 TestIfPlayerTouchesCustomElement(jx, jy);
4051 if (IS_CUSTOM_ELEMENT(element))
4052 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4053 player->index_bit, enter_side);
4055 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4056 player->index_bit, enter_side);
4059 void Explode(int ex, int ey, int phase, int mode)
4065 /* !!! eliminate this variable !!! */
4066 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4068 if (game.explosions_delayed)
4070 ExplodeField[ex][ey] = mode;
4074 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4076 int center_element = Feld[ex][ey];
4077 int artwork_element, explosion_element; /* set these values later */
4080 /* --- This is only really needed (and now handled) in "Impact()". --- */
4081 /* do not explode moving elements that left the explode field in time */
4082 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4083 center_element == EL_EMPTY &&
4084 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4089 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4090 if (mode == EX_TYPE_NORMAL ||
4091 mode == EX_TYPE_CENTER ||
4092 mode == EX_TYPE_CROSS)
4093 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4096 /* remove things displayed in background while burning dynamite */
4097 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4100 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4102 /* put moving element to center field (and let it explode there) */
4103 center_element = MovingOrBlocked2Element(ex, ey);
4104 RemoveMovingField(ex, ey);
4105 Feld[ex][ey] = center_element;
4108 /* now "center_element" is finally determined -- set related values now */
4109 artwork_element = center_element; /* for custom player artwork */
4110 explosion_element = center_element; /* for custom player artwork */
4112 if (IS_PLAYER(ex, ey))
4114 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4116 artwork_element = stored_player[player_nr].artwork_element;
4118 if (level.use_explosion_element[player_nr])
4120 explosion_element = level.explosion_element[player_nr];
4121 artwork_element = explosion_element;
4126 if (mode == EX_TYPE_NORMAL ||
4127 mode == EX_TYPE_CENTER ||
4128 mode == EX_TYPE_CROSS)
4129 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4133 last_phase = element_info[explosion_element].explosion_delay + 1;
4135 last_phase = element_info[center_element].explosion_delay + 1;
4138 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4140 int xx = x - ex + 1;
4141 int yy = y - ey + 1;
4144 if (!IN_LEV_FIELD(x, y) ||
4145 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4146 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4149 element = Feld[x][y];
4151 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4153 element = MovingOrBlocked2Element(x, y);
4155 if (!IS_EXPLOSION_PROOF(element))
4156 RemoveMovingField(x, y);
4159 /* indestructible elements can only explode in center (but not flames) */
4160 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4161 mode == EX_TYPE_BORDER)) ||
4162 element == EL_FLAMES)
4165 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4166 behaviour, for example when touching a yamyam that explodes to rocks
4167 with active deadly shield, a rock is created under the player !!! */
4168 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4170 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4171 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4172 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4174 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4177 if (IS_ACTIVE_BOMB(element))
4179 /* re-activate things under the bomb like gate or penguin */
4180 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4187 /* save walkable background elements while explosion on same tile */
4188 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4189 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4190 Back[x][y] = element;
4192 /* ignite explodable elements reached by other explosion */
4193 if (element == EL_EXPLOSION)
4194 element = Store2[x][y];
4196 if (AmoebaNr[x][y] &&
4197 (element == EL_AMOEBA_FULL ||
4198 element == EL_BD_AMOEBA ||
4199 element == EL_AMOEBA_GROWING))
4201 AmoebaCnt[AmoebaNr[x][y]]--;
4202 AmoebaCnt2[AmoebaNr[x][y]]--;
4207 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4210 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4212 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4214 switch(StorePlayer[ex][ey])
4217 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
4220 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
4223 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
4227 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
4232 if (PLAYERINFO(ex, ey)->use_murphy)
4233 Store[x][y] = EL_EMPTY;
4236 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4237 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4238 else if (ELEM_IS_PLAYER(center_element))
4239 Store[x][y] = EL_EMPTY;
4240 else if (center_element == EL_YAMYAM)
4241 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4242 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4243 Store[x][y] = element_info[center_element].content.e[xx][yy];
4245 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4246 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4247 otherwise) -- FIX THIS !!! */
4248 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4249 Store[x][y] = element_info[element].content.e[1][1];
4251 else if (!CAN_EXPLODE(element))
4252 Store[x][y] = element_info[element].content.e[1][1];
4255 Store[x][y] = EL_EMPTY;
4257 else if (center_element == EL_MOLE)
4258 Store[x][y] = EL_EMERALD_RED;
4259 else if (center_element == EL_PENGUIN)
4260 Store[x][y] = EL_EMERALD_PURPLE;
4261 else if (center_element == EL_BUG)
4262 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
4263 else if (center_element == EL_BD_BUTTERFLY)
4264 Store[x][y] = EL_BD_DIAMOND;
4265 else if (center_element == EL_SP_ELECTRON)
4266 Store[x][y] = EL_SP_INFOTRON;
4267 else if (center_element == EL_AMOEBA_TO_DIAMOND)
4268 Store[x][y] = level.amoeba_content;
4269 else if (center_element == EL_YAMYAM)
4270 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4271 else if (IS_CUSTOM_ELEMENT(center_element) &&
4272 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4273 Store[x][y] = element_info[center_element].content.e[xx][yy];
4274 else if (element == EL_WALL_EMERALD)
4275 Store[x][y] = EL_EMERALD;
4276 else if (element == EL_WALL_DIAMOND)
4277 Store[x][y] = EL_DIAMOND;
4278 else if (element == EL_WALL_BD_DIAMOND)
4279 Store[x][y] = EL_BD_DIAMOND;
4280 else if (element == EL_WALL_EMERALD_YELLOW)
4281 Store[x][y] = EL_EMERALD_YELLOW;
4282 else if (element == EL_WALL_EMERALD_RED)
4283 Store[x][y] = EL_EMERALD_RED;
4284 else if (element == EL_WALL_EMERALD_PURPLE)
4285 Store[x][y] = EL_EMERALD_PURPLE;
4286 else if (element == EL_WALL_PEARL)
4287 Store[x][y] = EL_PEARL;
4288 else if (element == EL_WALL_CRYSTAL)
4289 Store[x][y] = EL_CRYSTAL;
4290 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
4291 Store[x][y] = element_info[element].content.e[1][1];
4293 Store[x][y] = EL_EMPTY;
4296 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4297 center_element == EL_AMOEBA_TO_DIAMOND)
4298 Store2[x][y] = element;
4300 Feld[x][y] = EL_EXPLOSION;
4301 GfxElement[x][y] = artwork_element;
4304 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
4305 x, y, artwork_element, EL_NAME(artwork_element));
4308 ExplodePhase[x][y] = 1;
4309 ExplodeDelay[x][y] = last_phase;
4314 if (center_element == EL_YAMYAM)
4315 game.yamyam_content_nr =
4316 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4328 GfxFrame[x][y] = 0; /* restart explosion animation */
4330 last_phase = ExplodeDelay[x][y];
4332 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4336 /* activate this even in non-DEBUG version until cause for crash in
4337 getGraphicAnimationFrame() (see below) is found and eliminated */
4343 /* this can happen if the player leaves an explosion just in time */
4344 if (GfxElement[x][y] == EL_UNDEFINED)
4345 GfxElement[x][y] = EL_EMPTY;
4347 if (GfxElement[x][y] == EL_UNDEFINED)
4350 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4351 printf("Explode(): This should never happen!\n");
4354 GfxElement[x][y] = EL_EMPTY;
4360 border_element = Store2[x][y];
4361 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4362 border_element = StorePlayer[x][y];
4364 if (phase == element_info[border_element].ignition_delay ||
4365 phase == last_phase)
4367 boolean border_explosion = FALSE;
4369 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4370 !PLAYER_EXPLOSION_PROTECTED(x, y))
4372 KillPlayerUnlessExplosionProtected(x, y);
4373 border_explosion = TRUE;
4375 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4377 Feld[x][y] = Store2[x][y];
4380 border_explosion = TRUE;
4382 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4384 AmoebeUmwandeln(x, y);
4386 border_explosion = TRUE;
4389 /* if an element just explodes due to another explosion (chain-reaction),
4390 do not immediately end the new explosion when it was the last frame of
4391 the explosion (as it would be done in the following "if"-statement!) */
4392 if (border_explosion && phase == last_phase)
4396 if (phase == last_phase)
4400 element = Feld[x][y] = Store[x][y];
4401 Store[x][y] = Store2[x][y] = 0;
4402 GfxElement[x][y] = EL_UNDEFINED;
4404 /* player can escape from explosions and might therefore be still alive */
4405 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4406 element <= EL_PLAYER_IS_EXPLODING_4)
4408 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4409 int explosion_element = EL_PLAYER_1 + player_nr;
4410 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4411 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4413 if (level.use_explosion_element[player_nr])
4414 explosion_element = level.explosion_element[player_nr];
4416 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4417 element_info[explosion_element].content.e[xx][yy]);
4420 /* restore probably existing indestructible background element */
4421 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4422 element = Feld[x][y] = Back[x][y];
4425 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4426 GfxDir[x][y] = MV_NONE;
4427 ChangeDelay[x][y] = 0;
4428 ChangePage[x][y] = -1;
4430 #if USE_NEW_CUSTOM_VALUE
4431 CustomValue[x][y] = 0;
4434 InitField_WithBug2(x, y, FALSE);
4436 DrawLevelField(x, y);
4438 TestIfElementTouchesCustomElement(x, y);
4440 if (GFX_CRUMBLED(element))
4441 DrawLevelFieldCrumbledSandNeighbours(x, y);
4443 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4444 StorePlayer[x][y] = 0;
4446 if (ELEM_IS_PLAYER(element))
4447 RelocatePlayer(x, y, element);
4449 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4451 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4452 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4455 DrawLevelFieldCrumbledSand(x, y);
4457 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4459 DrawLevelElement(x, y, Back[x][y]);
4460 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4462 else if (IS_WALKABLE_UNDER(Back[x][y]))
4464 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4465 DrawLevelElementThruMask(x, y, Back[x][y]);
4467 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4468 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4472 void DynaExplode(int ex, int ey)
4475 int dynabomb_element = Feld[ex][ey];
4476 int dynabomb_size = 1;
4477 boolean dynabomb_xl = FALSE;
4478 struct PlayerInfo *player;
4479 static int xy[4][2] =
4487 if (IS_ACTIVE_BOMB(dynabomb_element))
4489 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4490 dynabomb_size = player->dynabomb_size;
4491 dynabomb_xl = player->dynabomb_xl;
4492 player->dynabombs_left++;
4495 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4497 for (i = 0; i < NUM_DIRECTIONS; i++)
4499 for (j = 1; j <= dynabomb_size; j++)
4501 int x = ex + j * xy[i][0];
4502 int y = ey + j * xy[i][1];
4505 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4508 element = Feld[x][y];
4510 /* do not restart explosions of fields with active bombs */
4511 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4514 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4516 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4517 !IS_DIGGABLE(element) && !dynabomb_xl)
4523 void Bang(int x, int y)
4525 int element = MovingOrBlocked2Element(x, y);
4526 int explosion_type = EX_TYPE_NORMAL;
4528 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4530 struct PlayerInfo *player = PLAYERINFO(x, y);
4532 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4533 player->element_nr);
4535 if (level.use_explosion_element[player->index_nr])
4537 int explosion_element = level.explosion_element[player->index_nr];
4539 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4540 explosion_type = EX_TYPE_CROSS;
4541 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4542 explosion_type = EX_TYPE_CENTER;
4550 case EL_BD_BUTTERFLY:
4553 case EL_DARK_YAMYAM:
4557 RaiseScoreElement(element);
4560 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4561 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4562 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4563 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4564 case EL_DYNABOMB_INCREASE_NUMBER:
4565 case EL_DYNABOMB_INCREASE_SIZE:
4566 case EL_DYNABOMB_INCREASE_POWER:
4567 explosion_type = EX_TYPE_DYNA;
4572 case EL_LAMP_ACTIVE:
4573 case EL_AMOEBA_TO_DIAMOND:
4574 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4575 explosion_type = EX_TYPE_CENTER;
4579 if (element_info[element].explosion_type == EXPLODES_CROSS)
4580 explosion_type = EX_TYPE_CROSS;
4581 else if (element_info[element].explosion_type == EXPLODES_1X1)
4582 explosion_type = EX_TYPE_CENTER;
4586 if (explosion_type == EX_TYPE_DYNA)
4589 Explode(x, y, EX_PHASE_START, explosion_type);
4591 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4594 void SplashAcid(int x, int y)
4596 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4597 (!IN_LEV_FIELD(x - 1, y - 2) ||
4598 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4599 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4601 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4602 (!IN_LEV_FIELD(x + 1, y - 2) ||
4603 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4604 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4606 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4609 static void InitBeltMovement()
4611 static int belt_base_element[4] =
4613 EL_CONVEYOR_BELT_1_LEFT,
4614 EL_CONVEYOR_BELT_2_LEFT,
4615 EL_CONVEYOR_BELT_3_LEFT,
4616 EL_CONVEYOR_BELT_4_LEFT
4618 static int belt_base_active_element[4] =
4620 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4621 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4622 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4623 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4628 /* set frame order for belt animation graphic according to belt direction */
4629 for (i = 0; i < NUM_BELTS; i++)
4633 for (j = 0; j < NUM_BELT_PARTS; j++)
4635 int element = belt_base_active_element[belt_nr] + j;
4636 int graphic = el2img(element);
4638 if (game.belt_dir[i] == MV_LEFT)
4639 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4641 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4646 SCAN_PLAYFIELD(x, y)
4648 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4651 int element = Feld[x][y];
4653 for (i = 0; i < NUM_BELTS; i++)
4655 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4657 int e_belt_nr = getBeltNrFromBeltElement(element);
4660 if (e_belt_nr == belt_nr)
4662 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4664 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4671 static void ToggleBeltSwitch(int x, int y)
4673 static int belt_base_element[4] =
4675 EL_CONVEYOR_BELT_1_LEFT,
4676 EL_CONVEYOR_BELT_2_LEFT,
4677 EL_CONVEYOR_BELT_3_LEFT,
4678 EL_CONVEYOR_BELT_4_LEFT
4680 static int belt_base_active_element[4] =
4682 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4683 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4684 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4685 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4687 static int belt_base_switch_element[4] =
4689 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4690 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4691 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4692 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4694 static int belt_move_dir[4] =
4702 int element = Feld[x][y];
4703 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4704 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4705 int belt_dir = belt_move_dir[belt_dir_nr];
4708 if (!IS_BELT_SWITCH(element))
4711 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4712 game.belt_dir[belt_nr] = belt_dir;
4714 if (belt_dir_nr == 3)
4717 /* set frame order for belt animation graphic according to belt direction */
4718 for (i = 0; i < NUM_BELT_PARTS; i++)
4720 int element = belt_base_active_element[belt_nr] + i;
4721 int graphic = el2img(element);
4723 if (belt_dir == MV_LEFT)
4724 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4726 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4730 SCAN_PLAYFIELD(xx, yy)
4732 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4735 int element = Feld[xx][yy];
4737 if (IS_BELT_SWITCH(element))
4739 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4741 if (e_belt_nr == belt_nr)
4743 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4744 DrawLevelField(xx, yy);
4747 else if (IS_BELT(element) && belt_dir != MV_NONE)
4749 int e_belt_nr = getBeltNrFromBeltElement(element);
4751 if (e_belt_nr == belt_nr)
4753 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4755 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4756 DrawLevelField(xx, yy);
4759 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4761 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4763 if (e_belt_nr == belt_nr)
4765 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4767 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4768 DrawLevelField(xx, yy);
4774 static void ToggleSwitchgateSwitch(int x, int y)
4778 game.switchgate_pos = !game.switchgate_pos;
4781 SCAN_PLAYFIELD(xx, yy)
4783 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4786 int element = Feld[xx][yy];
4788 #if !USE_BOTH_SWITCHGATE_SWITCHES
4789 if (element == EL_SWITCHGATE_SWITCH_UP ||
4790 element == EL_SWITCHGATE_SWITCH_DOWN)
4792 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4793 DrawLevelField(xx, yy);
4796 if (element == EL_SWITCHGATE_SWITCH_UP)
4798 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4799 DrawLevelField(xx, yy);
4801 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4803 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4804 DrawLevelField(xx, yy);
4807 else if (element == EL_SWITCHGATE_OPEN ||
4808 element == EL_SWITCHGATE_OPENING)
4810 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4812 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4814 else if (element == EL_SWITCHGATE_CLOSED ||
4815 element == EL_SWITCHGATE_CLOSING)
4817 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4819 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4824 static int getInvisibleActiveFromInvisibleElement(int element)
4826 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4827 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4828 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4832 static int getInvisibleFromInvisibleActiveElement(int element)
4834 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4835 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4836 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4840 static void RedrawAllLightSwitchesAndInvisibleElements()
4845 SCAN_PLAYFIELD(x, y)
4847 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4850 int element = Feld[x][y];
4852 if (element == EL_LIGHT_SWITCH &&
4853 game.light_time_left > 0)
4855 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4856 DrawLevelField(x, y);
4858 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4859 game.light_time_left == 0)
4861 Feld[x][y] = EL_LIGHT_SWITCH;
4862 DrawLevelField(x, y);
4864 else if (element == EL_EMC_DRIPPER &&
4865 game.light_time_left > 0)
4867 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4868 DrawLevelField(x, y);
4870 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4871 game.light_time_left == 0)
4873 Feld[x][y] = EL_EMC_DRIPPER;
4874 DrawLevelField(x, y);
4876 else if (element == EL_INVISIBLE_STEELWALL ||
4877 element == EL_INVISIBLE_WALL ||
4878 element == EL_INVISIBLE_SAND)
4880 if (game.light_time_left > 0)
4881 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4883 DrawLevelField(x, y);
4885 /* uncrumble neighbour fields, if needed */
4886 if (element == EL_INVISIBLE_SAND)
4887 DrawLevelFieldCrumbledSandNeighbours(x, y);
4889 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4890 element == EL_INVISIBLE_WALL_ACTIVE ||
4891 element == EL_INVISIBLE_SAND_ACTIVE)
4893 if (game.light_time_left == 0)
4894 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4896 DrawLevelField(x, y);
4898 /* re-crumble neighbour fields, if needed */
4899 if (element == EL_INVISIBLE_SAND)
4900 DrawLevelFieldCrumbledSandNeighbours(x, y);
4905 static void RedrawAllInvisibleElementsForLenses()
4910 SCAN_PLAYFIELD(x, y)
4912 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4915 int element = Feld[x][y];
4917 if (element == EL_EMC_DRIPPER &&
4918 game.lenses_time_left > 0)
4920 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4921 DrawLevelField(x, y);
4923 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4924 game.lenses_time_left == 0)
4926 Feld[x][y] = EL_EMC_DRIPPER;
4927 DrawLevelField(x, y);
4929 else if (element == EL_INVISIBLE_STEELWALL ||
4930 element == EL_INVISIBLE_WALL ||
4931 element == EL_INVISIBLE_SAND)
4933 if (game.lenses_time_left > 0)
4934 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4936 DrawLevelField(x, y);
4938 /* uncrumble neighbour fields, if needed */
4939 if (element == EL_INVISIBLE_SAND)
4940 DrawLevelFieldCrumbledSandNeighbours(x, y);
4942 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4943 element == EL_INVISIBLE_WALL_ACTIVE ||
4944 element == EL_INVISIBLE_SAND_ACTIVE)
4946 if (game.lenses_time_left == 0)
4947 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4949 DrawLevelField(x, y);
4951 /* re-crumble neighbour fields, if needed */
4952 if (element == EL_INVISIBLE_SAND)
4953 DrawLevelFieldCrumbledSandNeighbours(x, y);
4958 static void RedrawAllInvisibleElementsForMagnifier()
4963 SCAN_PLAYFIELD(x, y)
4965 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4968 int element = Feld[x][y];
4970 if (element == EL_EMC_FAKE_GRASS &&
4971 game.magnify_time_left > 0)
4973 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4974 DrawLevelField(x, y);
4976 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4977 game.magnify_time_left == 0)
4979 Feld[x][y] = EL_EMC_FAKE_GRASS;
4980 DrawLevelField(x, y);
4982 else if (IS_GATE_GRAY(element) &&
4983 game.magnify_time_left > 0)
4985 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4986 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4987 IS_EM_GATE_GRAY(element) ?
4988 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4989 IS_EMC_GATE_GRAY(element) ?
4990 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4992 DrawLevelField(x, y);
4994 else if (IS_GATE_GRAY_ACTIVE(element) &&
4995 game.magnify_time_left == 0)
4997 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4998 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4999 IS_EM_GATE_GRAY_ACTIVE(element) ?
5000 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5001 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5002 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5004 DrawLevelField(x, y);
5009 static void ToggleLightSwitch(int x, int y)
5011 int element = Feld[x][y];
5013 game.light_time_left =
5014 (element == EL_LIGHT_SWITCH ?
5015 level.time_light * FRAMES_PER_SECOND : 0);
5017 RedrawAllLightSwitchesAndInvisibleElements();
5020 static void ActivateTimegateSwitch(int x, int y)
5024 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5027 SCAN_PLAYFIELD(xx, yy)
5029 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5032 int element = Feld[xx][yy];
5034 if (element == EL_TIMEGATE_CLOSED ||
5035 element == EL_TIMEGATE_CLOSING)
5037 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5038 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
5042 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5044 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5045 DrawLevelField(xx, yy);
5051 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5054 void Impact(int x, int y)
5056 boolean last_line = (y == lev_fieldy - 1);
5057 boolean object_hit = FALSE;
5058 boolean impact = (last_line || object_hit);
5059 int element = Feld[x][y];
5060 int smashed = EL_STEELWALL;
5062 if (!last_line) /* check if element below was hit */
5064 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5067 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5068 MovDir[x][y + 1] != MV_DOWN ||
5069 MovPos[x][y + 1] <= TILEY / 2));
5071 /* do not smash moving elements that left the smashed field in time */
5072 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5073 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5076 #if USE_QUICKSAND_IMPACT_BUGFIX
5077 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5079 RemoveMovingField(x, y + 1);
5080 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5081 Feld[x][y + 2] = EL_ROCK;
5082 DrawLevelField(x, y + 2);
5089 smashed = MovingOrBlocked2Element(x, y + 1);
5091 impact = (last_line || object_hit);
5094 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5096 SplashAcid(x, y + 1);
5100 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5101 /* only reset graphic animation if graphic really changes after impact */
5103 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5105 ResetGfxAnimation(x, y);
5106 DrawLevelField(x, y);
5109 if (impact && CAN_EXPLODE_IMPACT(element))
5114 else if (impact && element == EL_PEARL)
5116 ResetGfxAnimation(x, y);
5118 Feld[x][y] = EL_PEARL_BREAKING;
5119 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5122 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5124 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5129 if (impact && element == EL_AMOEBA_DROP)
5131 if (object_hit && IS_PLAYER(x, y + 1))
5132 KillPlayerUnlessEnemyProtected(x, y + 1);
5133 else if (object_hit && smashed == EL_PENGUIN)
5137 Feld[x][y] = EL_AMOEBA_GROWING;
5138 Store[x][y] = EL_AMOEBA_WET;
5140 ResetRandomAnimationValue(x, y);
5145 if (object_hit) /* check which object was hit */
5147 if (CAN_PASS_MAGIC_WALL(element) &&
5148 (smashed == EL_MAGIC_WALL ||
5149 smashed == EL_BD_MAGIC_WALL))
5152 int activated_magic_wall =
5153 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5154 EL_BD_MAGIC_WALL_ACTIVE);
5156 /* activate magic wall / mill */
5158 SCAN_PLAYFIELD(xx, yy)
5160 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5162 if (Feld[xx][yy] == smashed)
5163 Feld[xx][yy] = activated_magic_wall;
5165 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5166 game.magic_wall_active = TRUE;
5168 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5169 SND_MAGIC_WALL_ACTIVATING :
5170 SND_BD_MAGIC_WALL_ACTIVATING));
5173 if (IS_PLAYER(x, y + 1))
5175 if (CAN_SMASH_PLAYER(element))
5177 KillPlayerUnlessEnemyProtected(x, y + 1);
5181 else if (smashed == EL_PENGUIN)
5183 if (CAN_SMASH_PLAYER(element))
5189 else if (element == EL_BD_DIAMOND)
5191 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5197 else if (((element == EL_SP_INFOTRON ||
5198 element == EL_SP_ZONK) &&
5199 (smashed == EL_SP_SNIKSNAK ||
5200 smashed == EL_SP_ELECTRON ||
5201 smashed == EL_SP_DISK_ORANGE)) ||
5202 (element == EL_SP_INFOTRON &&
5203 smashed == EL_SP_DISK_YELLOW))
5208 else if (CAN_SMASH_EVERYTHING(element))
5210 if (IS_CLASSIC_ENEMY(smashed) ||
5211 CAN_EXPLODE_SMASHED(smashed))
5216 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5218 if (smashed == EL_LAMP ||
5219 smashed == EL_LAMP_ACTIVE)
5224 else if (smashed == EL_NUT)
5226 Feld[x][y + 1] = EL_NUT_BREAKING;
5227 PlayLevelSound(x, y, SND_NUT_BREAKING);
5228 RaiseScoreElement(EL_NUT);
5231 else if (smashed == EL_PEARL)
5233 ResetGfxAnimation(x, y);
5235 Feld[x][y + 1] = EL_PEARL_BREAKING;
5236 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5239 else if (smashed == EL_DIAMOND)
5241 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5242 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5245 else if (IS_BELT_SWITCH(smashed))
5247 ToggleBeltSwitch(x, y + 1);
5249 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5250 smashed == EL_SWITCHGATE_SWITCH_DOWN)
5252 ToggleSwitchgateSwitch(x, y + 1);
5254 else if (smashed == EL_LIGHT_SWITCH ||
5255 smashed == EL_LIGHT_SWITCH_ACTIVE)
5257 ToggleLightSwitch(x, y + 1);
5262 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5265 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5267 CheckElementChangeBySide(x, y + 1, smashed, element,
5268 CE_SWITCHED, CH_SIDE_TOP);
5269 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5275 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5280 /* play sound of magic wall / mill */
5282 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5283 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5285 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5286 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5287 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5288 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5293 /* play sound of object that hits the ground */
5294 if (last_line || object_hit)
5295 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5298 inline static void TurnRoundExt(int x, int y)
5310 { 0, 0 }, { 0, 0 }, { 0, 0 },
5315 int left, right, back;
5319 { MV_DOWN, MV_UP, MV_RIGHT },
5320 { MV_UP, MV_DOWN, MV_LEFT },
5322 { MV_LEFT, MV_RIGHT, MV_DOWN },
5326 { MV_RIGHT, MV_LEFT, MV_UP }
5329 int element = Feld[x][y];
5330 int move_pattern = element_info[element].move_pattern;
5332 int old_move_dir = MovDir[x][y];
5333 int left_dir = turn[old_move_dir].left;
5334 int right_dir = turn[old_move_dir].right;
5335 int back_dir = turn[old_move_dir].back;
5337 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5338 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5339 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5340 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5342 int left_x = x + left_dx, left_y = y + left_dy;
5343 int right_x = x + right_dx, right_y = y + right_dy;
5344 int move_x = x + move_dx, move_y = y + move_dy;
5348 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5350 TestIfBadThingTouchesOtherBadThing(x, y);
5352 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5353 MovDir[x][y] = right_dir;
5354 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5355 MovDir[x][y] = left_dir;
5357 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5359 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5362 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5364 TestIfBadThingTouchesOtherBadThing(x, y);
5366 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5367 MovDir[x][y] = left_dir;
5368 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5369 MovDir[x][y] = right_dir;
5371 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5373 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5376 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5378 TestIfBadThingTouchesOtherBadThing(x, y);
5380 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5381 MovDir[x][y] = left_dir;
5382 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5383 MovDir[x][y] = right_dir;
5385 if (MovDir[x][y] != old_move_dir)
5388 else if (element == EL_YAMYAM)
5390 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5391 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5393 if (can_turn_left && can_turn_right)
5394 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5395 else if (can_turn_left)
5396 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5397 else if (can_turn_right)
5398 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5400 MovDir[x][y] = back_dir;
5402 MovDelay[x][y] = 16 + 16 * RND(3);
5404 else if (element == EL_DARK_YAMYAM)
5406 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5408 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5411 if (can_turn_left && can_turn_right)
5412 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5413 else if (can_turn_left)
5414 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5415 else if (can_turn_right)
5416 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5418 MovDir[x][y] = back_dir;
5420 MovDelay[x][y] = 16 + 16 * RND(3);
5422 else if (element == EL_PACMAN)
5424 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5425 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5427 if (can_turn_left && can_turn_right)
5428 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5429 else if (can_turn_left)
5430 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5431 else if (can_turn_right)
5432 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5434 MovDir[x][y] = back_dir;
5436 MovDelay[x][y] = 6 + RND(40);
5438 else if (element == EL_PIG)
5440 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5441 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5442 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5443 boolean should_turn_left, should_turn_right, should_move_on;
5445 int rnd = RND(rnd_value);
5447 should_turn_left = (can_turn_left &&
5449 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5450 y + back_dy + left_dy)));
5451 should_turn_right = (can_turn_right &&
5453 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5454 y + back_dy + right_dy)));
5455 should_move_on = (can_move_on &&
5458 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5459 y + move_dy + left_dy) ||
5460 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5461 y + move_dy + right_dy)));
5463 if (should_turn_left || should_turn_right || should_move_on)
5465 if (should_turn_left && should_turn_right && should_move_on)
5466 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5467 rnd < 2 * rnd_value / 3 ? right_dir :
5469 else if (should_turn_left && should_turn_right)
5470 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5471 else if (should_turn_left && should_move_on)
5472 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5473 else if (should_turn_right && should_move_on)
5474 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5475 else if (should_turn_left)
5476 MovDir[x][y] = left_dir;
5477 else if (should_turn_right)
5478 MovDir[x][y] = right_dir;
5479 else if (should_move_on)
5480 MovDir[x][y] = old_move_dir;
5482 else if (can_move_on && rnd > rnd_value / 8)
5483 MovDir[x][y] = old_move_dir;
5484 else if (can_turn_left && can_turn_right)
5485 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5486 else if (can_turn_left && rnd > rnd_value / 8)
5487 MovDir[x][y] = left_dir;
5488 else if (can_turn_right && rnd > rnd_value/8)
5489 MovDir[x][y] = right_dir;
5491 MovDir[x][y] = back_dir;
5493 xx = x + move_xy[MovDir[x][y]].dx;
5494 yy = y + move_xy[MovDir[x][y]].dy;
5496 if (!IN_LEV_FIELD(xx, yy) ||
5497 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5498 MovDir[x][y] = old_move_dir;
5502 else if (element == EL_DRAGON)
5504 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5505 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5506 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5508 int rnd = RND(rnd_value);
5510 if (can_move_on && rnd > rnd_value / 8)
5511 MovDir[x][y] = old_move_dir;
5512 else if (can_turn_left && can_turn_right)
5513 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5514 else if (can_turn_left && rnd > rnd_value / 8)
5515 MovDir[x][y] = left_dir;
5516 else if (can_turn_right && rnd > rnd_value / 8)
5517 MovDir[x][y] = right_dir;
5519 MovDir[x][y] = back_dir;
5521 xx = x + move_xy[MovDir[x][y]].dx;
5522 yy = y + move_xy[MovDir[x][y]].dy;
5524 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5525 MovDir[x][y] = old_move_dir;
5529 else if (element == EL_MOLE)
5531 boolean can_move_on =
5532 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5533 IS_AMOEBOID(Feld[move_x][move_y]) ||
5534 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5537 boolean can_turn_left =
5538 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5539 IS_AMOEBOID(Feld[left_x][left_y])));
5541 boolean can_turn_right =
5542 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5543 IS_AMOEBOID(Feld[right_x][right_y])));
5545 if (can_turn_left && can_turn_right)
5546 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5547 else if (can_turn_left)
5548 MovDir[x][y] = left_dir;
5550 MovDir[x][y] = right_dir;
5553 if (MovDir[x][y] != old_move_dir)
5556 else if (element == EL_BALLOON)
5558 MovDir[x][y] = game.wind_direction;
5561 else if (element == EL_SPRING)
5563 #if USE_NEW_SPRING_BUMPER
5564 if (MovDir[x][y] & MV_HORIZONTAL)
5566 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5567 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5569 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5570 ResetGfxAnimation(move_x, move_y);
5571 DrawLevelField(move_x, move_y);
5573 MovDir[x][y] = back_dir;
5575 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5576 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5577 MovDir[x][y] = MV_NONE;
5580 if (MovDir[x][y] & MV_HORIZONTAL &&
5581 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5582 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5583 MovDir[x][y] = MV_NONE;
5588 else if (element == EL_ROBOT ||
5589 element == EL_SATELLITE ||
5590 element == EL_PENGUIN ||
5591 element == EL_EMC_ANDROID)
5593 int attr_x = -1, attr_y = -1;
5604 for (i = 0; i < MAX_PLAYERS; i++)
5606 struct PlayerInfo *player = &stored_player[i];
5607 int jx = player->jx, jy = player->jy;
5609 if (!player->active)
5613 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5621 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5622 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5623 game.engine_version < VERSION_IDENT(3,1,0,0)))
5629 if (element == EL_PENGUIN)
5632 static int xy[4][2] =
5640 for (i = 0; i < NUM_DIRECTIONS; i++)
5642 int ex = x + xy[i][0];
5643 int ey = y + xy[i][1];
5645 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5654 MovDir[x][y] = MV_NONE;
5656 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5657 else if (attr_x > x)
5658 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5660 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5661 else if (attr_y > y)
5662 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5664 if (element == EL_ROBOT)
5668 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5669 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5670 Moving2Blocked(x, y, &newx, &newy);
5672 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5673 MovDelay[x][y] = 8 + 8 * !RND(3);
5675 MovDelay[x][y] = 16;
5677 else if (element == EL_PENGUIN)
5683 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5685 boolean first_horiz = RND(2);
5686 int new_move_dir = MovDir[x][y];
5689 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5690 Moving2Blocked(x, y, &newx, &newy);
5692 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5696 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5697 Moving2Blocked(x, y, &newx, &newy);
5699 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5702 MovDir[x][y] = old_move_dir;
5706 else if (element == EL_SATELLITE)
5712 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5714 boolean first_horiz = RND(2);
5715 int new_move_dir = MovDir[x][y];
5718 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5719 Moving2Blocked(x, y, &newx, &newy);
5721 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5725 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5726 Moving2Blocked(x, y, &newx, &newy);
5728 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5731 MovDir[x][y] = old_move_dir;
5735 else if (element == EL_EMC_ANDROID)
5737 static int check_pos[16] =
5739 -1, /* 0 => (invalid) */
5740 7, /* 1 => MV_LEFT */
5741 3, /* 2 => MV_RIGHT */
5742 -1, /* 3 => (invalid) */
5744 0, /* 5 => MV_LEFT | MV_UP */
5745 2, /* 6 => MV_RIGHT | MV_UP */
5746 -1, /* 7 => (invalid) */
5747 5, /* 8 => MV_DOWN */
5748 6, /* 9 => MV_LEFT | MV_DOWN */
5749 4, /* 10 => MV_RIGHT | MV_DOWN */
5750 -1, /* 11 => (invalid) */
5751 -1, /* 12 => (invalid) */
5752 -1, /* 13 => (invalid) */
5753 -1, /* 14 => (invalid) */
5754 -1, /* 15 => (invalid) */
5762 { -1, -1, MV_LEFT | MV_UP },
5764 { +1, -1, MV_RIGHT | MV_UP },
5765 { +1, 0, MV_RIGHT },
5766 { +1, +1, MV_RIGHT | MV_DOWN },
5768 { -1, +1, MV_LEFT | MV_DOWN },
5771 int start_pos, check_order;
5772 boolean can_clone = FALSE;
5775 /* check if there is any free field around current position */
5776 for (i = 0; i < 8; i++)
5778 int newx = x + check_xy[i].dx;
5779 int newy = y + check_xy[i].dy;
5781 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5789 if (can_clone) /* randomly find an element to clone */
5793 start_pos = check_pos[RND(8)];
5794 check_order = (RND(2) ? -1 : +1);
5796 for (i = 0; i < 8; i++)
5798 int pos_raw = start_pos + i * check_order;
5799 int pos = (pos_raw + 8) % 8;
5800 int newx = x + check_xy[pos].dx;
5801 int newy = y + check_xy[pos].dy;
5803 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5805 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5806 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5808 Store[x][y] = Feld[newx][newy];
5817 if (can_clone) /* randomly find a direction to move */
5821 start_pos = check_pos[RND(8)];
5822 check_order = (RND(2) ? -1 : +1);
5824 for (i = 0; i < 8; i++)
5826 int pos_raw = start_pos + i * check_order;
5827 int pos = (pos_raw + 8) % 8;
5828 int newx = x + check_xy[pos].dx;
5829 int newy = y + check_xy[pos].dy;
5830 int new_move_dir = check_xy[pos].dir;
5832 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5834 MovDir[x][y] = new_move_dir;
5835 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5844 if (can_clone) /* cloning and moving successful */
5847 /* cannot clone -- try to move towards player */
5849 start_pos = check_pos[MovDir[x][y] & 0x0f];
5850 check_order = (RND(2) ? -1 : +1);
5852 for (i = 0; i < 3; i++)
5854 /* first check start_pos, then previous/next or (next/previous) pos */
5855 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5856 int pos = (pos_raw + 8) % 8;
5857 int newx = x + check_xy[pos].dx;
5858 int newy = y + check_xy[pos].dy;
5859 int new_move_dir = check_xy[pos].dir;
5861 if (IS_PLAYER(newx, newy))
5864 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5866 MovDir[x][y] = new_move_dir;
5867 MovDelay[x][y] = level.android_move_time * 8 + 1;
5874 else if (move_pattern == MV_TURNING_LEFT ||
5875 move_pattern == MV_TURNING_RIGHT ||
5876 move_pattern == MV_TURNING_LEFT_RIGHT ||
5877 move_pattern == MV_TURNING_RIGHT_LEFT ||
5878 move_pattern == MV_TURNING_RANDOM ||
5879 move_pattern == MV_ALL_DIRECTIONS)
5881 boolean can_turn_left =
5882 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5883 boolean can_turn_right =
5884 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5886 if (element_info[element].move_stepsize == 0) /* "not moving" */
5889 if (move_pattern == MV_TURNING_LEFT)
5890 MovDir[x][y] = left_dir;
5891 else if (move_pattern == MV_TURNING_RIGHT)
5892 MovDir[x][y] = right_dir;
5893 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5894 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5895 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5896 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5897 else if (move_pattern == MV_TURNING_RANDOM)
5898 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5899 can_turn_right && !can_turn_left ? right_dir :
5900 RND(2) ? left_dir : right_dir);
5901 else if (can_turn_left && can_turn_right)
5902 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5903 else if (can_turn_left)
5904 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5905 else if (can_turn_right)
5906 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5908 MovDir[x][y] = back_dir;
5910 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5912 else if (move_pattern == MV_HORIZONTAL ||
5913 move_pattern == MV_VERTICAL)
5915 if (move_pattern & old_move_dir)
5916 MovDir[x][y] = back_dir;
5917 else if (move_pattern == MV_HORIZONTAL)
5918 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5919 else if (move_pattern == MV_VERTICAL)
5920 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5922 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5924 else if (move_pattern & MV_ANY_DIRECTION)
5926 MovDir[x][y] = move_pattern;
5927 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5929 else if (move_pattern & MV_WIND_DIRECTION)
5931 MovDir[x][y] = game.wind_direction;
5932 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5934 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5936 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5937 MovDir[x][y] = left_dir;
5938 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5939 MovDir[x][y] = right_dir;
5941 if (MovDir[x][y] != old_move_dir)
5942 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5944 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5946 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5947 MovDir[x][y] = right_dir;
5948 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5949 MovDir[x][y] = left_dir;
5951 if (MovDir[x][y] != old_move_dir)
5952 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5954 else if (move_pattern == MV_TOWARDS_PLAYER ||
5955 move_pattern == MV_AWAY_FROM_PLAYER)
5957 int attr_x = -1, attr_y = -1;
5959 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5970 for (i = 0; i < MAX_PLAYERS; i++)
5972 struct PlayerInfo *player = &stored_player[i];
5973 int jx = player->jx, jy = player->jy;
5975 if (!player->active)
5979 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5987 MovDir[x][y] = MV_NONE;
5989 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5990 else if (attr_x > x)
5991 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5993 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5994 else if (attr_y > y)
5995 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5997 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5999 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6001 boolean first_horiz = RND(2);
6002 int new_move_dir = MovDir[x][y];
6004 if (element_info[element].move_stepsize == 0) /* "not moving" */
6006 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6007 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6013 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6014 Moving2Blocked(x, y, &newx, &newy);
6016 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6020 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6021 Moving2Blocked(x, y, &newx, &newy);
6023 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6026 MovDir[x][y] = old_move_dir;
6029 else if (move_pattern == MV_WHEN_PUSHED ||
6030 move_pattern == MV_WHEN_DROPPED)
6032 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6033 MovDir[x][y] = MV_NONE;
6037 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6039 static int test_xy[7][2] =
6049 static int test_dir[7] =
6059 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6060 int move_preference = -1000000; /* start with very low preference */
6061 int new_move_dir = MV_NONE;
6062 int start_test = RND(4);
6065 for (i = 0; i < NUM_DIRECTIONS; i++)
6067 int move_dir = test_dir[start_test + i];
6068 int move_dir_preference;
6070 xx = x + test_xy[start_test + i][0];
6071 yy = y + test_xy[start_test + i][1];
6073 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6074 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6076 new_move_dir = move_dir;
6081 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6084 move_dir_preference = -1 * RunnerVisit[xx][yy];
6085 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6086 move_dir_preference = PlayerVisit[xx][yy];
6088 if (move_dir_preference > move_preference)
6090 /* prefer field that has not been visited for the longest time */
6091 move_preference = move_dir_preference;
6092 new_move_dir = move_dir;
6094 else if (move_dir_preference == move_preference &&
6095 move_dir == old_move_dir)
6097 /* prefer last direction when all directions are preferred equally */
6098 move_preference = move_dir_preference;
6099 new_move_dir = move_dir;
6103 MovDir[x][y] = new_move_dir;
6104 if (old_move_dir != new_move_dir)
6105 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6109 static void TurnRound(int x, int y)
6111 int direction = MovDir[x][y];
6113 int element, graphic;
6118 GfxDir[x][y] = MovDir[x][y];
6120 if (direction != MovDir[x][y])
6124 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6127 ResetGfxFrame(x, y, FALSE);
6129 element = Feld[x][y];
6130 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6132 if (graphic_info[graphic].anim_global_sync)
6133 GfxFrame[x][y] = FrameCounter;
6134 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
6135 GfxFrame[x][y] = CustomValue[x][y];
6136 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
6137 GfxFrame[x][y] = element_info[element].collect_score;
6138 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
6139 GfxFrame[x][y] = ChangeDelay[x][y];
6143 static boolean JustBeingPushed(int x, int y)
6147 for (i = 0; i < MAX_PLAYERS; i++)
6149 struct PlayerInfo *player = &stored_player[i];
6151 if (player->active && player->is_pushing && player->MovPos)
6153 int next_jx = player->jx + (player->jx - player->last_jx);
6154 int next_jy = player->jy + (player->jy - player->last_jy);
6156 if (x == next_jx && y == next_jy)
6164 void StartMoving(int x, int y)
6166 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6167 int element = Feld[x][y];
6172 if (MovDelay[x][y] == 0)
6173 GfxAction[x][y] = ACTION_DEFAULT;
6175 if (CAN_FALL(element) && y < lev_fieldy - 1)
6177 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6178 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6179 if (JustBeingPushed(x, y))
6182 if (element == EL_QUICKSAND_FULL)
6184 if (IS_FREE(x, y + 1))
6186 InitMovingField(x, y, MV_DOWN);
6187 started_moving = TRUE;
6189 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6190 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6191 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6192 Store[x][y] = EL_ROCK;
6194 Store[x][y] = EL_ROCK;
6197 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6199 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6201 if (!MovDelay[x][y])
6202 MovDelay[x][y] = TILEY + 1;
6211 Feld[x][y] = EL_QUICKSAND_EMPTY;
6212 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6213 Store[x][y + 1] = Store[x][y];
6216 PlayLevelSoundAction(x, y, ACTION_FILLING);
6219 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6220 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6222 InitMovingField(x, y, MV_DOWN);
6223 started_moving = TRUE;
6225 Feld[x][y] = EL_QUICKSAND_FILLING;
6226 Store[x][y] = element;
6228 PlayLevelSoundAction(x, y, ACTION_FILLING);
6230 else if (element == EL_MAGIC_WALL_FULL)
6232 if (IS_FREE(x, y + 1))
6234 InitMovingField(x, y, MV_DOWN);
6235 started_moving = TRUE;
6237 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6238 Store[x][y] = EL_CHANGED(Store[x][y]);
6240 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6242 if (!MovDelay[x][y])
6243 MovDelay[x][y] = TILEY/4 + 1;
6252 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6253 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6254 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6258 else if (element == EL_BD_MAGIC_WALL_FULL)
6260 if (IS_FREE(x, y + 1))
6262 InitMovingField(x, y, MV_DOWN);
6263 started_moving = TRUE;
6265 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6266 Store[x][y] = EL_CHANGED2(Store[x][y]);
6268 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6270 if (!MovDelay[x][y])
6271 MovDelay[x][y] = TILEY/4 + 1;
6280 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6281 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6282 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
6286 else if (CAN_PASS_MAGIC_WALL(element) &&
6287 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6288 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
6290 InitMovingField(x, y, MV_DOWN);
6291 started_moving = TRUE;
6294 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6295 EL_BD_MAGIC_WALL_FILLING);
6296 Store[x][y] = element;
6298 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6300 SplashAcid(x, y + 1);
6302 InitMovingField(x, y, MV_DOWN);
6303 started_moving = TRUE;
6305 Store[x][y] = EL_ACID;
6307 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6308 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6310 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6311 CAN_FALL(element) && WasJustFalling[x][y] &&
6312 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6314 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6315 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6316 (Feld[x][y + 1] == EL_BLOCKED)))
6318 /* this is needed for a special case not covered by calling "Impact()"
6319 from "ContinueMoving()": if an element moves to a tile directly below
6320 another element which was just falling on that tile (which was empty
6321 in the previous frame), the falling element above would just stop
6322 instead of smashing the element below (in previous version, the above
6323 element was just checked for "moving" instead of "falling", resulting
6324 in incorrect smashes caused by horizontal movement of the above
6325 element; also, the case of the player being the element to smash was
6326 simply not covered here... :-/ ) */
6328 CheckCollision[x][y] = 0;
6332 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6334 if (MovDir[x][y] == MV_NONE)
6336 InitMovingField(x, y, MV_DOWN);
6337 started_moving = TRUE;
6340 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6342 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6343 MovDir[x][y] = MV_DOWN;
6345 InitMovingField(x, y, MV_DOWN);
6346 started_moving = TRUE;
6348 else if (element == EL_AMOEBA_DROP)
6350 Feld[x][y] = EL_AMOEBA_GROWING;
6351 Store[x][y] = EL_AMOEBA_WET;
6353 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6354 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6355 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6356 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6358 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6359 (IS_FREE(x - 1, y + 1) ||
6360 Feld[x - 1][y + 1] == EL_ACID));
6361 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6362 (IS_FREE(x + 1, y + 1) ||
6363 Feld[x + 1][y + 1] == EL_ACID));
6364 boolean can_fall_any = (can_fall_left || can_fall_right);
6365 boolean can_fall_both = (can_fall_left && can_fall_right);
6366 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6368 #if USE_NEW_ALL_SLIPPERY
6369 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6371 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6372 can_fall_right = FALSE;
6373 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6374 can_fall_left = FALSE;
6375 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6376 can_fall_right = FALSE;
6377 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6378 can_fall_left = FALSE;
6380 can_fall_any = (can_fall_left || can_fall_right);
6381 can_fall_both = FALSE;
6384 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6386 if (slippery_type == SLIPPERY_ONLY_LEFT)
6387 can_fall_right = FALSE;
6388 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6389 can_fall_left = FALSE;
6390 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6391 can_fall_right = FALSE;
6392 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6393 can_fall_left = FALSE;
6395 can_fall_any = (can_fall_left || can_fall_right);
6396 can_fall_both = (can_fall_left && can_fall_right);
6400 #if USE_NEW_ALL_SLIPPERY
6402 #if USE_NEW_SP_SLIPPERY
6403 /* !!! better use the same properties as for custom elements here !!! */
6404 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6405 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6407 can_fall_right = FALSE; /* slip down on left side */
6408 can_fall_both = FALSE;
6413 #if USE_NEW_ALL_SLIPPERY
6416 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6417 can_fall_right = FALSE; /* slip down on left side */
6419 can_fall_left = !(can_fall_right = RND(2));
6421 can_fall_both = FALSE;
6426 if (game.emulation == EMU_BOULDERDASH ||
6427 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6428 can_fall_right = FALSE; /* slip down on left side */
6430 can_fall_left = !(can_fall_right = RND(2));
6432 can_fall_both = FALSE;
6438 /* if not determined otherwise, prefer left side for slipping down */
6439 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6440 started_moving = TRUE;
6444 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6446 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6449 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6450 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6451 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6452 int belt_dir = game.belt_dir[belt_nr];
6454 if ((belt_dir == MV_LEFT && left_is_free) ||
6455 (belt_dir == MV_RIGHT && right_is_free))
6457 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6459 InitMovingField(x, y, belt_dir);
6460 started_moving = TRUE;
6462 Pushed[x][y] = TRUE;
6463 Pushed[nextx][y] = TRUE;
6465 GfxAction[x][y] = ACTION_DEFAULT;
6469 MovDir[x][y] = 0; /* if element was moving, stop it */
6474 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6476 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6478 if (CAN_MOVE(element) && !started_moving)
6481 int move_pattern = element_info[element].move_pattern;
6486 if (MovDir[x][y] == MV_NONE)
6488 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6489 x, y, element, element_info[element].token_name);
6490 printf("StartMoving(): This should never happen!\n");
6495 Moving2Blocked(x, y, &newx, &newy);
6497 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6500 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6501 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6503 WasJustMoving[x][y] = 0;
6504 CheckCollision[x][y] = 0;
6506 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6508 if (Feld[x][y] != element) /* element has changed */
6512 if (!MovDelay[x][y]) /* start new movement phase */
6514 /* all objects that can change their move direction after each step
6515 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6517 if (element != EL_YAMYAM &&
6518 element != EL_DARK_YAMYAM &&
6519 element != EL_PACMAN &&
6520 !(move_pattern & MV_ANY_DIRECTION) &&
6521 move_pattern != MV_TURNING_LEFT &&
6522 move_pattern != MV_TURNING_RIGHT &&
6523 move_pattern != MV_TURNING_LEFT_RIGHT &&
6524 move_pattern != MV_TURNING_RIGHT_LEFT &&
6525 move_pattern != MV_TURNING_RANDOM)
6529 if (MovDelay[x][y] && (element == EL_BUG ||
6530 element == EL_SPACESHIP ||
6531 element == EL_SP_SNIKSNAK ||
6532 element == EL_SP_ELECTRON ||
6533 element == EL_MOLE))
6534 DrawLevelField(x, y);
6538 if (MovDelay[x][y]) /* wait some time before next movement */
6542 if (element == EL_ROBOT ||
6543 element == EL_YAMYAM ||
6544 element == EL_DARK_YAMYAM)
6546 DrawLevelElementAnimationIfNeeded(x, y, element);
6547 PlayLevelSoundAction(x, y, ACTION_WAITING);
6549 else if (element == EL_SP_ELECTRON)
6550 DrawLevelElementAnimationIfNeeded(x, y, element);
6551 else if (element == EL_DRAGON)
6554 int dir = MovDir[x][y];
6555 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6556 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6557 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6558 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6559 dir == MV_UP ? IMG_FLAMES_1_UP :
6560 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6561 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6563 GfxAction[x][y] = ACTION_ATTACKING;
6565 if (IS_PLAYER(x, y))
6566 DrawPlayerField(x, y);
6568 DrawLevelField(x, y);
6570 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6572 for (i = 1; i <= 3; i++)
6574 int xx = x + i * dx;
6575 int yy = y + i * dy;
6576 int sx = SCREENX(xx);
6577 int sy = SCREENY(yy);
6578 int flame_graphic = graphic + (i - 1);
6580 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6585 int flamed = MovingOrBlocked2Element(xx, yy);
6589 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6591 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6592 RemoveMovingField(xx, yy);
6594 RemoveField(xx, yy);
6596 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6599 RemoveMovingField(xx, yy);
6602 ChangeDelay[xx][yy] = 0;
6604 Feld[xx][yy] = EL_FLAMES;
6606 if (IN_SCR_FIELD(sx, sy))
6608 DrawLevelFieldCrumbledSand(xx, yy);
6609 DrawGraphic(sx, sy, flame_graphic, frame);
6614 if (Feld[xx][yy] == EL_FLAMES)
6615 Feld[xx][yy] = EL_EMPTY;
6616 DrawLevelField(xx, yy);
6621 if (MovDelay[x][y]) /* element still has to wait some time */
6623 PlayLevelSoundAction(x, y, ACTION_WAITING);
6629 /* now make next step */
6631 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6633 if (DONT_COLLIDE_WITH(element) &&
6634 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6635 !PLAYER_ENEMY_PROTECTED(newx, newy))
6637 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6642 else if (CAN_MOVE_INTO_ACID(element) &&
6643 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6644 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6645 (MovDir[x][y] == MV_DOWN ||
6646 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6648 SplashAcid(newx, newy);
6649 Store[x][y] = EL_ACID;
6651 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6653 if (Feld[newx][newy] == EL_EXIT_OPEN)
6656 DrawLevelField(x, y);
6658 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6659 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6660 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6662 local_player->friends_still_needed--;
6663 if (!local_player->friends_still_needed &&
6664 !local_player->GameOver && AllPlayersGone)
6665 local_player->LevelSolved = local_player->GameOver = TRUE;
6669 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6671 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6672 DrawLevelField(newx, newy);
6674 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6676 else if (!IS_FREE(newx, newy))
6678 GfxAction[x][y] = ACTION_WAITING;
6680 if (IS_PLAYER(x, y))
6681 DrawPlayerField(x, y);
6683 DrawLevelField(x, y);
6688 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6690 if (IS_FOOD_PIG(Feld[newx][newy]))
6692 if (IS_MOVING(newx, newy))
6693 RemoveMovingField(newx, newy);
6696 Feld[newx][newy] = EL_EMPTY;
6697 DrawLevelField(newx, newy);
6700 PlayLevelSound(x, y, SND_PIG_DIGGING);
6702 else if (!IS_FREE(newx, newy))
6704 if (IS_PLAYER(x, y))
6705 DrawPlayerField(x, y);
6707 DrawLevelField(x, y);
6712 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6714 if (Store[x][y] != EL_EMPTY)
6716 boolean can_clone = FALSE;
6719 /* check if element to clone is still there */
6720 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6722 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6730 /* cannot clone or target field not free anymore -- do not clone */
6731 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6732 Store[x][y] = EL_EMPTY;
6735 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6737 if (IS_MV_DIAGONAL(MovDir[x][y]))
6739 int diagonal_move_dir = MovDir[x][y];
6740 int stored = Store[x][y];
6741 int change_delay = 8;
6744 /* android is moving diagonally */
6746 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6748 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6749 GfxElement[x][y] = EL_EMC_ANDROID;
6750 GfxAction[x][y] = ACTION_SHRINKING;
6751 GfxDir[x][y] = diagonal_move_dir;
6752 ChangeDelay[x][y] = change_delay;
6754 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6757 DrawLevelGraphicAnimation(x, y, graphic);
6758 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6760 if (Feld[newx][newy] == EL_ACID)
6762 SplashAcid(newx, newy);
6767 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6769 Store[newx][newy] = EL_EMC_ANDROID;
6770 GfxElement[newx][newy] = EL_EMC_ANDROID;
6771 GfxAction[newx][newy] = ACTION_GROWING;
6772 GfxDir[newx][newy] = diagonal_move_dir;
6773 ChangeDelay[newx][newy] = change_delay;
6775 graphic = el_act_dir2img(GfxElement[newx][newy],
6776 GfxAction[newx][newy], GfxDir[newx][newy]);
6778 DrawLevelGraphicAnimation(newx, newy, graphic);
6779 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6785 Feld[newx][newy] = EL_EMPTY;
6786 DrawLevelField(newx, newy);
6788 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6791 else if (!IS_FREE(newx, newy))
6794 if (IS_PLAYER(x, y))
6795 DrawPlayerField(x, y);
6797 DrawLevelField(x, y);
6803 else if (IS_CUSTOM_ELEMENT(element) &&
6804 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6806 int new_element = Feld[newx][newy];
6808 if (!IS_FREE(newx, newy))
6810 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6811 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6814 /* no element can dig solid indestructible elements */
6815 if (IS_INDESTRUCTIBLE(new_element) &&
6816 !IS_DIGGABLE(new_element) &&
6817 !IS_COLLECTIBLE(new_element))
6820 if (AmoebaNr[newx][newy] &&
6821 (new_element == EL_AMOEBA_FULL ||
6822 new_element == EL_BD_AMOEBA ||
6823 new_element == EL_AMOEBA_GROWING))
6825 AmoebaCnt[AmoebaNr[newx][newy]]--;
6826 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6829 if (IS_MOVING(newx, newy))
6830 RemoveMovingField(newx, newy);
6833 RemoveField(newx, newy);
6834 DrawLevelField(newx, newy);
6837 /* if digged element was about to explode, prevent the explosion */
6838 ExplodeField[newx][newy] = EX_TYPE_NONE;
6840 PlayLevelSoundAction(x, y, action);
6843 Store[newx][newy] = EL_EMPTY;
6845 /* this makes it possible to leave the removed element again */
6846 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6847 Store[newx][newy] = new_element;
6849 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6851 int move_leave_element = element_info[element].move_leave_element;
6853 /* this makes it possible to leave the removed element again */
6854 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6855 new_element : move_leave_element);
6859 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6861 RunnerVisit[x][y] = FrameCounter;
6862 PlayerVisit[x][y] /= 8; /* expire player visit path */
6865 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6867 if (!IS_FREE(newx, newy))
6869 if (IS_PLAYER(x, y))
6870 DrawPlayerField(x, y);
6872 DrawLevelField(x, y);
6878 boolean wanna_flame = !RND(10);
6879 int dx = newx - x, dy = newy - y;
6880 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6881 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6882 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6883 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6884 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6885 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6888 IS_CLASSIC_ENEMY(element1) ||
6889 IS_CLASSIC_ENEMY(element2)) &&
6890 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6891 element1 != EL_FLAMES && element2 != EL_FLAMES)
6893 ResetGfxAnimation(x, y);
6894 GfxAction[x][y] = ACTION_ATTACKING;
6896 if (IS_PLAYER(x, y))
6897 DrawPlayerField(x, y);
6899 DrawLevelField(x, y);
6901 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6903 MovDelay[x][y] = 50;
6907 RemoveField(newx, newy);
6909 Feld[newx][newy] = EL_FLAMES;
6910 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6913 RemoveField(newx1, newy1);
6915 Feld[newx1][newy1] = EL_FLAMES;
6917 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6920 RemoveField(newx2, newy2);
6922 Feld[newx2][newy2] = EL_FLAMES;
6929 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6930 Feld[newx][newy] == EL_DIAMOND)
6932 if (IS_MOVING(newx, newy))
6933 RemoveMovingField(newx, newy);
6936 Feld[newx][newy] = EL_EMPTY;
6937 DrawLevelField(newx, newy);
6940 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6942 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6943 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6945 if (AmoebaNr[newx][newy])
6947 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6948 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6949 Feld[newx][newy] == EL_BD_AMOEBA)
6950 AmoebaCnt[AmoebaNr[newx][newy]]--;
6955 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6957 RemoveMovingField(newx, newy);
6960 if (IS_MOVING(newx, newy))
6962 RemoveMovingField(newx, newy);
6967 Feld[newx][newy] = EL_EMPTY;
6968 DrawLevelField(newx, newy);
6971 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6973 else if ((element == EL_PACMAN || element == EL_MOLE)
6974 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6976 if (AmoebaNr[newx][newy])
6978 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6979 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6980 Feld[newx][newy] == EL_BD_AMOEBA)
6981 AmoebaCnt[AmoebaNr[newx][newy]]--;
6984 if (element == EL_MOLE)
6986 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6987 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6989 ResetGfxAnimation(x, y);
6990 GfxAction[x][y] = ACTION_DIGGING;
6991 DrawLevelField(x, y);
6993 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6995 return; /* wait for shrinking amoeba */
6997 else /* element == EL_PACMAN */
6999 Feld[newx][newy] = EL_EMPTY;
7000 DrawLevelField(newx, newy);
7001 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7004 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7005 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7006 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7008 /* wait for shrinking amoeba to completely disappear */
7011 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7013 /* object was running against a wall */
7018 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7019 if (move_pattern & MV_ANY_DIRECTION &&
7020 move_pattern == MovDir[x][y])
7022 int blocking_element =
7023 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7025 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7028 element = Feld[x][y]; /* element might have changed */
7032 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7033 DrawLevelElementAnimation(x, y, element);
7035 if (DONT_TOUCH(element))
7036 TestIfBadThingTouchesPlayer(x, y);
7041 InitMovingField(x, y, MovDir[x][y]);
7043 PlayLevelSoundAction(x, y, ACTION_MOVING);
7047 ContinueMoving(x, y);
7050 void ContinueMoving(int x, int y)
7052 int element = Feld[x][y];
7053 struct ElementInfo *ei = &element_info[element];
7054 int direction = MovDir[x][y];
7055 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7056 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7057 int newx = x + dx, newy = y + dy;
7058 int stored = Store[x][y];
7059 int stored_new = Store[newx][newy];
7060 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7061 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7062 boolean last_line = (newy == lev_fieldy - 1);
7064 MovPos[x][y] += getElementMoveStepsize(x, y);
7066 if (pushed_by_player) /* special case: moving object pushed by player */
7067 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7069 if (ABS(MovPos[x][y]) < TILEX)
7071 DrawLevelField(x, y);
7073 return; /* element is still moving */
7076 /* element reached destination field */
7078 Feld[x][y] = EL_EMPTY;
7079 Feld[newx][newy] = element;
7080 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7082 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7084 element = Feld[newx][newy] = EL_ACID;
7086 else if (element == EL_MOLE)
7088 Feld[x][y] = EL_SAND;
7090 DrawLevelFieldCrumbledSandNeighbours(x, y);
7092 else if (element == EL_QUICKSAND_FILLING)
7094 element = Feld[newx][newy] = get_next_element(element);
7095 Store[newx][newy] = Store[x][y];
7097 else if (element == EL_QUICKSAND_EMPTYING)
7099 Feld[x][y] = get_next_element(element);
7100 element = Feld[newx][newy] = Store[x][y];
7102 else if (element == EL_MAGIC_WALL_FILLING)
7104 element = Feld[newx][newy] = get_next_element(element);
7105 if (!game.magic_wall_active)
7106 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7107 Store[newx][newy] = Store[x][y];
7109 else if (element == EL_MAGIC_WALL_EMPTYING)
7111 Feld[x][y] = get_next_element(element);
7112 if (!game.magic_wall_active)
7113 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7114 element = Feld[newx][newy] = Store[x][y];
7116 #if USE_NEW_CUSTOM_VALUE
7117 InitField(newx, newy, FALSE);
7120 else if (element == EL_BD_MAGIC_WALL_FILLING)
7122 element = Feld[newx][newy] = get_next_element(element);
7123 if (!game.magic_wall_active)
7124 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7125 Store[newx][newy] = Store[x][y];
7127 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7129 Feld[x][y] = get_next_element(element);
7130 if (!game.magic_wall_active)
7131 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7132 element = Feld[newx][newy] = Store[x][y];
7134 #if USE_NEW_CUSTOM_VALUE
7135 InitField(newx, newy, FALSE);
7138 else if (element == EL_AMOEBA_DROPPING)
7140 Feld[x][y] = get_next_element(element);
7141 element = Feld[newx][newy] = Store[x][y];
7143 else if (element == EL_SOKOBAN_OBJECT)
7146 Feld[x][y] = Back[x][y];
7148 if (Back[newx][newy])
7149 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7151 Back[x][y] = Back[newx][newy] = 0;
7154 Store[x][y] = EL_EMPTY;
7159 MovDelay[newx][newy] = 0;
7162 if (CAN_CHANGE_OR_HAS_ACTION(element))
7164 if (CAN_CHANGE(element))
7167 /* copy element change control values to new field */
7168 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7169 ChangePage[newx][newy] = ChangePage[x][y];
7170 ChangeCount[newx][newy] = ChangeCount[x][y];
7171 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7174 #if USE_NEW_CUSTOM_VALUE
7175 CustomValue[newx][newy] = CustomValue[x][y];
7181 #if USE_NEW_CUSTOM_VALUE
7182 CustomValue[newx][newy] = CustomValue[x][y];
7186 ChangeDelay[x][y] = 0;
7187 ChangePage[x][y] = -1;
7188 ChangeCount[x][y] = 0;
7189 ChangeEvent[x][y] = -1;
7191 #if USE_NEW_CUSTOM_VALUE
7192 CustomValue[x][y] = 0;
7195 /* copy animation control values to new field */
7196 GfxFrame[newx][newy] = GfxFrame[x][y];
7197 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7198 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7199 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7201 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7203 /* some elements can leave other elements behind after moving */
7205 if (ei->move_leave_element != EL_EMPTY &&
7206 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7207 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7209 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7210 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7211 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7214 int move_leave_element = ei->move_leave_element;
7218 /* this makes it possible to leave the removed element again */
7219 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7220 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7222 /* this makes it possible to leave the removed element again */
7223 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7224 move_leave_element = stored;
7227 /* this makes it possible to leave the removed element again */
7228 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7229 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7230 move_leave_element = stored;
7233 Feld[x][y] = move_leave_element;
7235 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7236 MovDir[x][y] = direction;
7238 InitField(x, y, FALSE);
7240 if (GFX_CRUMBLED(Feld[x][y]))
7241 DrawLevelFieldCrumbledSandNeighbours(x, y);
7243 if (ELEM_IS_PLAYER(move_leave_element))
7244 RelocatePlayer(x, y, move_leave_element);
7247 /* do this after checking for left-behind element */
7248 ResetGfxAnimation(x, y); /* reset animation values for old field */
7250 if (!CAN_MOVE(element) ||
7251 (CAN_FALL(element) && direction == MV_DOWN &&
7252 (element == EL_SPRING ||
7253 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7254 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7255 GfxDir[x][y] = MovDir[newx][newy] = 0;
7257 DrawLevelField(x, y);
7258 DrawLevelField(newx, newy);
7260 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7262 /* prevent pushed element from moving on in pushed direction */
7263 if (pushed_by_player && CAN_MOVE(element) &&
7264 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7265 !(element_info[element].move_pattern & direction))
7266 TurnRound(newx, newy);
7268 /* prevent elements on conveyor belt from moving on in last direction */
7269 if (pushed_by_conveyor && CAN_FALL(element) &&
7270 direction & MV_HORIZONTAL)
7271 MovDir[newx][newy] = 0;
7273 if (!pushed_by_player)
7275 int nextx = newx + dx, nexty = newy + dy;
7276 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7278 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7280 if (CAN_FALL(element) && direction == MV_DOWN)
7281 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7283 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7284 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7287 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7289 TestIfBadThingTouchesPlayer(newx, newy);
7290 TestIfBadThingTouchesFriend(newx, newy);
7292 if (!IS_CUSTOM_ELEMENT(element))
7293 TestIfBadThingTouchesOtherBadThing(newx, newy);
7295 else if (element == EL_PENGUIN)
7296 TestIfFriendTouchesBadThing(newx, newy);
7298 /* give the player one last chance (one more frame) to move away */
7299 if (CAN_FALL(element) && direction == MV_DOWN &&
7300 (last_line || (!IS_FREE(x, newy + 1) &&
7301 (!IS_PLAYER(x, newy + 1) ||
7302 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7305 if (pushed_by_player && !game.use_change_when_pushing_bug)
7307 int push_side = MV_DIR_OPPOSITE(direction);
7308 struct PlayerInfo *player = PLAYERINFO(x, y);
7310 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7311 player->index_bit, push_side);
7312 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7313 player->index_bit, push_side);
7316 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7317 MovDelay[newx][newy] = 1;
7319 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7321 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7324 if (ChangePage[newx][newy] != -1) /* delayed change */
7326 int page = ChangePage[newx][newy];
7327 struct ElementChangeInfo *change = &ei->change_page[page];
7329 ChangePage[newx][newy] = -1;
7331 if (change->can_change)
7333 if (ChangeElement(newx, newy, element, page))
7335 if (change->post_change_function)
7336 change->post_change_function(newx, newy);
7340 if (change->has_action)
7341 ExecuteCustomElementAction(newx, newy, element, page);
7345 TestIfElementHitsCustomElement(newx, newy, direction);
7346 TestIfPlayerTouchesCustomElement(newx, newy);
7347 TestIfElementTouchesCustomElement(newx, newy);
7350 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7351 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7352 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7353 MV_DIR_OPPOSITE(direction));
7357 int AmoebeNachbarNr(int ax, int ay)
7360 int element = Feld[ax][ay];
7362 static int xy[4][2] =
7370 for (i = 0; i < NUM_DIRECTIONS; i++)
7372 int x = ax + xy[i][0];
7373 int y = ay + xy[i][1];
7375 if (!IN_LEV_FIELD(x, y))
7378 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7379 group_nr = AmoebaNr[x][y];
7385 void AmoebenVereinigen(int ax, int ay)
7387 int i, x, y, xx, yy;
7388 int new_group_nr = AmoebaNr[ax][ay];
7389 static int xy[4][2] =
7397 if (new_group_nr == 0)
7400 for (i = 0; i < NUM_DIRECTIONS; i++)
7405 if (!IN_LEV_FIELD(x, y))
7408 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7409 Feld[x][y] == EL_BD_AMOEBA ||
7410 Feld[x][y] == EL_AMOEBA_DEAD) &&
7411 AmoebaNr[x][y] != new_group_nr)
7413 int old_group_nr = AmoebaNr[x][y];
7415 if (old_group_nr == 0)
7418 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7419 AmoebaCnt[old_group_nr] = 0;
7420 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7421 AmoebaCnt2[old_group_nr] = 0;
7424 SCAN_PLAYFIELD(xx, yy)
7426 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7429 if (AmoebaNr[xx][yy] == old_group_nr)
7430 AmoebaNr[xx][yy] = new_group_nr;
7436 void AmoebeUmwandeln(int ax, int ay)
7440 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7442 int group_nr = AmoebaNr[ax][ay];
7447 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7448 printf("AmoebeUmwandeln(): This should never happen!\n");
7454 SCAN_PLAYFIELD(x, y)
7456 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7459 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7462 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7466 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7467 SND_AMOEBA_TURNING_TO_GEM :
7468 SND_AMOEBA_TURNING_TO_ROCK));
7473 static int xy[4][2] =
7481 for (i = 0; i < NUM_DIRECTIONS; i++)
7486 if (!IN_LEV_FIELD(x, y))
7489 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7491 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7492 SND_AMOEBA_TURNING_TO_GEM :
7493 SND_AMOEBA_TURNING_TO_ROCK));
7500 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7503 int group_nr = AmoebaNr[ax][ay];
7504 boolean done = FALSE;
7509 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7510 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7516 SCAN_PLAYFIELD(x, y)
7518 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7521 if (AmoebaNr[x][y] == group_nr &&
7522 (Feld[x][y] == EL_AMOEBA_DEAD ||
7523 Feld[x][y] == EL_BD_AMOEBA ||
7524 Feld[x][y] == EL_AMOEBA_GROWING))
7527 Feld[x][y] = new_element;
7528 InitField(x, y, FALSE);
7529 DrawLevelField(x, y);
7535 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7536 SND_BD_AMOEBA_TURNING_TO_ROCK :
7537 SND_BD_AMOEBA_TURNING_TO_GEM));
7540 void AmoebeWaechst(int x, int y)
7542 static unsigned long sound_delay = 0;
7543 static unsigned long sound_delay_value = 0;
7545 if (!MovDelay[x][y]) /* start new growing cycle */
7549 if (DelayReached(&sound_delay, sound_delay_value))
7551 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7552 sound_delay_value = 30;
7556 if (MovDelay[x][y]) /* wait some time before growing bigger */
7559 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7561 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7562 6 - MovDelay[x][y]);
7564 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7567 if (!MovDelay[x][y])
7569 Feld[x][y] = Store[x][y];
7571 DrawLevelField(x, y);
7576 void AmoebaDisappearing(int x, int y)
7578 static unsigned long sound_delay = 0;
7579 static unsigned long sound_delay_value = 0;
7581 if (!MovDelay[x][y]) /* start new shrinking cycle */
7585 if (DelayReached(&sound_delay, sound_delay_value))
7586 sound_delay_value = 30;
7589 if (MovDelay[x][y]) /* wait some time before shrinking */
7592 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7594 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7595 6 - MovDelay[x][y]);
7597 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7600 if (!MovDelay[x][y])
7602 Feld[x][y] = EL_EMPTY;
7603 DrawLevelField(x, y);
7605 /* don't let mole enter this field in this cycle;
7606 (give priority to objects falling to this field from above) */
7612 void AmoebeAbleger(int ax, int ay)
7615 int element = Feld[ax][ay];
7616 int graphic = el2img(element);
7617 int newax = ax, neway = ay;
7618 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7619 static int xy[4][2] =
7627 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7629 Feld[ax][ay] = EL_AMOEBA_DEAD;
7630 DrawLevelField(ax, ay);
7634 if (IS_ANIMATED(graphic))
7635 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7637 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7638 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7640 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7643 if (MovDelay[ax][ay])
7647 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7650 int x = ax + xy[start][0];
7651 int y = ay + xy[start][1];
7653 if (!IN_LEV_FIELD(x, y))
7656 if (IS_FREE(x, y) ||
7657 CAN_GROW_INTO(Feld[x][y]) ||
7658 Feld[x][y] == EL_QUICKSAND_EMPTY)
7664 if (newax == ax && neway == ay)
7667 else /* normal or "filled" (BD style) amoeba */
7670 boolean waiting_for_player = FALSE;
7672 for (i = 0; i < NUM_DIRECTIONS; i++)
7674 int j = (start + i) % 4;
7675 int x = ax + xy[j][0];
7676 int y = ay + xy[j][1];
7678 if (!IN_LEV_FIELD(x, y))
7681 if (IS_FREE(x, y) ||
7682 CAN_GROW_INTO(Feld[x][y]) ||
7683 Feld[x][y] == EL_QUICKSAND_EMPTY)
7689 else if (IS_PLAYER(x, y))
7690 waiting_for_player = TRUE;
7693 if (newax == ax && neway == ay) /* amoeba cannot grow */
7695 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7697 Feld[ax][ay] = EL_AMOEBA_DEAD;
7698 DrawLevelField(ax, ay);
7699 AmoebaCnt[AmoebaNr[ax][ay]]--;
7701 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7703 if (element == EL_AMOEBA_FULL)
7704 AmoebeUmwandeln(ax, ay);
7705 else if (element == EL_BD_AMOEBA)
7706 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7711 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7713 /* amoeba gets larger by growing in some direction */
7715 int new_group_nr = AmoebaNr[ax][ay];
7718 if (new_group_nr == 0)
7720 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7721 printf("AmoebeAbleger(): This should never happen!\n");
7726 AmoebaNr[newax][neway] = new_group_nr;
7727 AmoebaCnt[new_group_nr]++;
7728 AmoebaCnt2[new_group_nr]++;
7730 /* if amoeba touches other amoeba(s) after growing, unify them */
7731 AmoebenVereinigen(newax, neway);
7733 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7735 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7741 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7742 (neway == lev_fieldy - 1 && newax != ax))
7744 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7745 Store[newax][neway] = element;
7747 else if (neway == ay || element == EL_EMC_DRIPPER)
7749 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7751 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7755 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7756 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7757 Store[ax][ay] = EL_AMOEBA_DROP;
7758 ContinueMoving(ax, ay);
7762 DrawLevelField(newax, neway);
7765 void Life(int ax, int ay)
7769 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7772 int element = Feld[ax][ay];
7773 int graphic = el2img(element);
7774 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7776 boolean changed = FALSE;
7778 if (IS_ANIMATED(graphic))
7779 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7784 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7785 MovDelay[ax][ay] = life_time;
7787 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7790 if (MovDelay[ax][ay])
7794 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7796 int xx = ax+x1, yy = ay+y1;
7799 if (!IN_LEV_FIELD(xx, yy))
7802 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7804 int x = xx+x2, y = yy+y2;
7806 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7809 if (((Feld[x][y] == element ||
7810 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7812 (IS_FREE(x, y) && Stop[x][y]))
7816 if (xx == ax && yy == ay) /* field in the middle */
7818 if (nachbarn < life_parameter[0] ||
7819 nachbarn > life_parameter[1])
7821 Feld[xx][yy] = EL_EMPTY;
7823 DrawLevelField(xx, yy);
7824 Stop[xx][yy] = TRUE;
7828 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7829 { /* free border field */
7830 if (nachbarn >= life_parameter[2] &&
7831 nachbarn <= life_parameter[3])
7833 Feld[xx][yy] = element;
7834 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7836 DrawLevelField(xx, yy);
7837 Stop[xx][yy] = TRUE;
7844 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7845 SND_GAME_OF_LIFE_GROWING);
7848 static void InitRobotWheel(int x, int y)
7850 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7853 static void RunRobotWheel(int x, int y)
7855 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7858 static void StopRobotWheel(int x, int y)
7860 if (ZX == x && ZY == y)
7864 static void InitTimegateWheel(int x, int y)
7866 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7869 static void RunTimegateWheel(int x, int y)
7871 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7874 static void InitMagicBallDelay(int x, int y)
7877 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7879 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7883 static void ActivateMagicBall(int bx, int by)
7887 if (level.ball_random)
7889 int pos_border = RND(8); /* select one of the eight border elements */
7890 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7891 int xx = pos_content % 3;
7892 int yy = pos_content / 3;
7897 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7898 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7902 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7904 int xx = x - bx + 1;
7905 int yy = y - by + 1;
7907 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7908 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7912 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7915 static void InitDiagonalMovingElement(int x, int y)
7918 MovDelay[x][y] = level.android_move_time;
7922 void CheckExit(int x, int y)
7924 if (local_player->gems_still_needed > 0 ||
7925 local_player->sokobanfields_still_needed > 0 ||
7926 local_player->lights_still_needed > 0)
7928 int element = Feld[x][y];
7929 int graphic = el2img(element);
7931 if (IS_ANIMATED(graphic))
7932 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7937 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7940 Feld[x][y] = EL_EXIT_OPENING;
7942 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7945 void CheckExitSP(int x, int y)
7947 if (local_player->gems_still_needed > 0)
7949 int element = Feld[x][y];
7950 int graphic = el2img(element);
7952 if (IS_ANIMATED(graphic))
7953 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7958 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7961 Feld[x][y] = EL_SP_EXIT_OPENING;
7963 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7966 static void CloseAllOpenTimegates()
7971 SCAN_PLAYFIELD(x, y)
7973 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7976 int element = Feld[x][y];
7978 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7980 Feld[x][y] = EL_TIMEGATE_CLOSING;
7982 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7987 void EdelsteinFunkeln(int x, int y)
7989 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7992 if (Feld[x][y] == EL_BD_DIAMOND)
7995 if (MovDelay[x][y] == 0) /* next animation frame */
7996 MovDelay[x][y] = 11 * !SimpleRND(500);
7998 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8002 if (setup.direct_draw && MovDelay[x][y])
8003 SetDrawtoField(DRAW_BUFFERED);
8005 DrawLevelElementAnimation(x, y, Feld[x][y]);
8007 if (MovDelay[x][y] != 0)
8009 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8010 10 - MovDelay[x][y]);
8012 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8014 if (setup.direct_draw)
8018 dest_x = FX + SCREENX(x) * TILEX;
8019 dest_y = FY + SCREENY(y) * TILEY;
8021 BlitBitmap(drawto_field, window,
8022 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8023 SetDrawtoField(DRAW_DIRECT);
8029 void MauerWaechst(int x, int y)
8033 if (!MovDelay[x][y]) /* next animation frame */
8034 MovDelay[x][y] = 3 * delay;
8036 if (MovDelay[x][y]) /* wait some time before next frame */
8040 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8042 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8043 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8045 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8048 if (!MovDelay[x][y])
8050 if (MovDir[x][y] == MV_LEFT)
8052 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8053 DrawLevelField(x - 1, y);
8055 else if (MovDir[x][y] == MV_RIGHT)
8057 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8058 DrawLevelField(x + 1, y);
8060 else if (MovDir[x][y] == MV_UP)
8062 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8063 DrawLevelField(x, y - 1);
8067 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8068 DrawLevelField(x, y + 1);
8071 Feld[x][y] = Store[x][y];
8073 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8074 DrawLevelField(x, y);
8079 void MauerAbleger(int ax, int ay)
8081 int element = Feld[ax][ay];
8082 int graphic = el2img(element);
8083 boolean oben_frei = FALSE, unten_frei = FALSE;
8084 boolean links_frei = FALSE, rechts_frei = FALSE;
8085 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8086 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8087 boolean new_wall = FALSE;
8089 if (IS_ANIMATED(graphic))
8090 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8092 if (!MovDelay[ax][ay]) /* start building new wall */
8093 MovDelay[ax][ay] = 6;
8095 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8098 if (MovDelay[ax][ay])
8102 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8104 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8106 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8108 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8111 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8112 element == EL_EXPANDABLE_WALL_ANY)
8116 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8117 Store[ax][ay-1] = element;
8118 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8119 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8120 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8121 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8126 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8127 Store[ax][ay+1] = element;
8128 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8129 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8130 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8131 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8136 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8137 element == EL_EXPANDABLE_WALL_ANY ||
8138 element == EL_EXPANDABLE_WALL ||
8139 element == EL_BD_EXPANDABLE_WALL)
8143 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8144 Store[ax-1][ay] = element;
8145 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8146 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8147 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8148 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8154 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8155 Store[ax+1][ay] = element;
8156 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8157 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8158 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8159 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8164 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8165 DrawLevelField(ax, ay);
8167 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8169 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8170 unten_massiv = TRUE;
8171 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8172 links_massiv = TRUE;
8173 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8174 rechts_massiv = TRUE;
8176 if (((oben_massiv && unten_massiv) ||
8177 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8178 element == EL_EXPANDABLE_WALL) &&
8179 ((links_massiv && rechts_massiv) ||
8180 element == EL_EXPANDABLE_WALL_VERTICAL))
8181 Feld[ax][ay] = EL_WALL;
8184 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8187 void CheckForDragon(int x, int y)
8190 boolean dragon_found = FALSE;
8191 static int xy[4][2] =
8199 for (i = 0; i < NUM_DIRECTIONS; i++)
8201 for (j = 0; j < 4; j++)
8203 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8205 if (IN_LEV_FIELD(xx, yy) &&
8206 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8208 if (Feld[xx][yy] == EL_DRAGON)
8209 dragon_found = TRUE;
8218 for (i = 0; i < NUM_DIRECTIONS; i++)
8220 for (j = 0; j < 3; j++)
8222 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8224 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8226 Feld[xx][yy] = EL_EMPTY;
8227 DrawLevelField(xx, yy);
8236 static void InitBuggyBase(int x, int y)
8238 int element = Feld[x][y];
8239 int activating_delay = FRAMES_PER_SECOND / 4;
8242 (element == EL_SP_BUGGY_BASE ?
8243 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8244 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8246 element == EL_SP_BUGGY_BASE_ACTIVE ?
8247 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8250 static void WarnBuggyBase(int x, int y)
8253 static int xy[4][2] =
8261 for (i = 0; i < NUM_DIRECTIONS; i++)
8263 int xx = x + xy[i][0];
8264 int yy = y + xy[i][1];
8266 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8268 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8275 static void InitTrap(int x, int y)
8277 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8280 static void ActivateTrap(int x, int y)
8282 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8285 static void ChangeActiveTrap(int x, int y)
8287 int graphic = IMG_TRAP_ACTIVE;
8289 /* if new animation frame was drawn, correct crumbled sand border */
8290 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8291 DrawLevelFieldCrumbledSand(x, y);
8294 static int getSpecialActionElement(int element, int number, int base_element)
8296 return (element != EL_EMPTY ? element :
8297 number != -1 ? base_element + number - 1 :
8301 static int getModifiedActionNumber(int value_old, int operator, int operand,
8302 int value_min, int value_max)
8304 int value_new = (operator == CA_MODE_SET ? operand :
8305 operator == CA_MODE_ADD ? value_old + operand :
8306 operator == CA_MODE_SUBTRACT ? value_old - operand :
8307 operator == CA_MODE_MULTIPLY ? value_old * operand :
8308 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8309 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8312 return (value_new < value_min ? value_min :
8313 value_new > value_max ? value_max :
8317 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8319 struct ElementInfo *ei = &element_info[element];
8320 struct ElementChangeInfo *change = &ei->change_page[page];
8321 int target_element = change->target_element;
8322 int action_type = change->action_type;
8323 int action_mode = change->action_mode;
8324 int action_arg = change->action_arg;
8327 if (!change->has_action)
8330 /* ---------- determine action paramater values -------------------------- */
8332 int level_time_value =
8333 (level.time > 0 ? TimeLeft :
8336 int action_arg_element =
8337 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8338 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8339 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8342 int action_arg_direction =
8343 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8344 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8345 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8346 change->actual_trigger_side :
8347 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8348 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8351 int action_arg_number_min =
8352 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8355 int action_arg_number_max =
8356 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8357 action_type == CA_SET_LEVEL_GEMS ? 999 :
8358 action_type == CA_SET_LEVEL_TIME ? 9999 :
8359 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8360 action_type == CA_SET_CE_VALUE ? 9999 :
8361 action_type == CA_SET_CE_SCORE ? 9999 :
8364 int action_arg_number_reset =
8365 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8366 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8367 action_type == CA_SET_LEVEL_TIME ? level.time :
8368 action_type == CA_SET_LEVEL_SCORE ? 0 :
8370 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8372 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8374 action_type == CA_SET_CE_SCORE ? 0 :
8377 int action_arg_number =
8378 (action_arg <= CA_ARG_MAX ? action_arg :
8379 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8380 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8381 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8382 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8383 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8384 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8385 #if USE_NEW_CUSTOM_VALUE
8386 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8388 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8390 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8391 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8392 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8393 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8394 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8395 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8396 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8397 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8398 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8399 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8400 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8403 int action_arg_number_old =
8404 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8405 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8406 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8407 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8408 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8411 int action_arg_number_new =
8412 getModifiedActionNumber(action_arg_number_old,
8413 action_mode, action_arg_number,
8414 action_arg_number_min, action_arg_number_max);
8416 int trigger_player_bits =
8417 (change->actual_trigger_player >= EL_PLAYER_1 &&
8418 change->actual_trigger_player <= EL_PLAYER_4 ?
8419 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8422 int action_arg_player_bits =
8423 (action_arg >= CA_ARG_PLAYER_1 &&
8424 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8425 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8428 /* ---------- execute action -------------------------------------------- */
8437 /* ---------- level actions ------------------------------------------- */
8439 case CA_RESTART_LEVEL:
8441 game.restart_level = TRUE;
8446 case CA_SHOW_ENVELOPE:
8448 int element = getSpecialActionElement(action_arg_element,
8449 action_arg_number, EL_ENVELOPE_1);
8451 if (IS_ENVELOPE(element))
8452 local_player->show_envelope = element;
8457 case CA_SET_LEVEL_TIME:
8459 if (level.time > 0) /* only modify limited time value */
8461 TimeLeft = action_arg_number_new;
8463 DrawGameValue_Time(TimeLeft);
8465 if (!TimeLeft && setup.time_limit)
8466 for (i = 0; i < MAX_PLAYERS; i++)
8467 KillPlayer(&stored_player[i]);
8473 case CA_SET_LEVEL_SCORE:
8475 local_player->score = action_arg_number_new;
8477 DrawGameValue_Score(local_player->score);
8482 case CA_SET_LEVEL_GEMS:
8484 local_player->gems_still_needed = action_arg_number_new;
8486 DrawGameValue_Emeralds(local_player->gems_still_needed);
8491 #if !USE_PLAYER_GRAVITY
8492 case CA_SET_LEVEL_GRAVITY:
8494 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8495 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8496 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8502 case CA_SET_LEVEL_WIND:
8504 game.wind_direction = action_arg_direction;
8509 /* ---------- player actions ------------------------------------------ */
8511 case CA_MOVE_PLAYER:
8513 /* automatically move to the next field in specified direction */
8514 for (i = 0; i < MAX_PLAYERS; i++)
8515 if (trigger_player_bits & (1 << i))
8516 stored_player[i].programmed_action = action_arg_direction;
8521 case CA_EXIT_PLAYER:
8523 for (i = 0; i < MAX_PLAYERS; i++)
8524 if (action_arg_player_bits & (1 << i))
8525 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8530 case CA_KILL_PLAYER:
8532 for (i = 0; i < MAX_PLAYERS; i++)
8533 if (action_arg_player_bits & (1 << i))
8534 KillPlayer(&stored_player[i]);
8539 case CA_SET_PLAYER_KEYS:
8541 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8542 int element = getSpecialActionElement(action_arg_element,
8543 action_arg_number, EL_KEY_1);
8545 if (IS_KEY(element))
8547 for (i = 0; i < MAX_PLAYERS; i++)
8549 if (trigger_player_bits & (1 << i))
8551 stored_player[i].key[KEY_NR(element)] = key_state;
8554 DrawGameDoorValues();
8556 DrawGameValue_Keys(stored_player[i].key);
8559 redraw_mask |= REDRAW_DOOR_1;
8567 case CA_SET_PLAYER_SPEED:
8569 for (i = 0; i < MAX_PLAYERS; i++)
8571 if (trigger_player_bits & (1 << i))
8573 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8575 if (action_arg == CA_ARG_SPEED_FASTER &&
8576 stored_player[i].cannot_move)
8578 action_arg_number = STEPSIZE_VERY_SLOW;
8580 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8581 action_arg == CA_ARG_SPEED_FASTER)
8583 action_arg_number = 2;
8584 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8587 else if (action_arg == CA_ARG_NUMBER_RESET)
8589 action_arg_number = level.initial_player_stepsize[i];
8593 getModifiedActionNumber(move_stepsize,
8596 action_arg_number_min,
8597 action_arg_number_max);
8600 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8602 /* make sure that value is power of 2 */
8603 move_stepsize = (1 << log_2(move_stepsize));
8605 /* do no immediately change -- the player might just be moving */
8606 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8608 stored_player[i].cannot_move =
8609 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8617 case CA_SET_PLAYER_SHIELD:
8619 for (i = 0; i < MAX_PLAYERS; i++)
8621 if (trigger_player_bits & (1 << i))
8623 if (action_arg == CA_ARG_SHIELD_OFF)
8625 stored_player[i].shield_normal_time_left = 0;
8626 stored_player[i].shield_deadly_time_left = 0;
8628 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8630 stored_player[i].shield_normal_time_left = 999999;
8632 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8634 stored_player[i].shield_normal_time_left = 999999;
8635 stored_player[i].shield_deadly_time_left = 999999;
8643 #if USE_PLAYER_GRAVITY
8644 case CA_SET_PLAYER_GRAVITY:
8646 for (i = 0; i < MAX_PLAYERS; i++)
8648 if (trigger_player_bits & (1 << i))
8650 stored_player[i].gravity =
8651 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8652 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8653 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8654 stored_player[i].gravity);
8662 case CA_SET_PLAYER_ARTWORK:
8664 for (i = 0; i < MAX_PLAYERS; i++)
8666 if (trigger_player_bits & (1 << i))
8668 int artwork_element = action_arg_element;
8670 if (action_arg == CA_ARG_ELEMENT_RESET)
8672 (level.use_artwork_element[i] ? level.artwork_element[i] :
8673 stored_player[i].element_nr);
8675 stored_player[i].artwork_element = artwork_element;
8677 SetPlayerWaiting(&stored_player[i], FALSE);
8679 /* set number of special actions for bored and sleeping animation */
8680 stored_player[i].num_special_action_bored =
8681 get_num_special_action(artwork_element,
8682 ACTION_BORING_1, ACTION_BORING_LAST);
8683 stored_player[i].num_special_action_sleeping =
8684 get_num_special_action(artwork_element,
8685 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8692 /* ---------- CE actions ---------------------------------------------- */
8694 case CA_SET_CE_VALUE:
8696 #if USE_NEW_CUSTOM_VALUE
8697 int last_ce_value = CustomValue[x][y];
8699 CustomValue[x][y] = action_arg_number_new;
8702 printf("::: CE value == %d\n", CustomValue[x][y]);
8705 if (CustomValue[x][y] != last_ce_value)
8707 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8708 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8710 if (CustomValue[x][y] == 0)
8713 printf("::: CE_VALUE_GETS_ZERO\n");
8716 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8717 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8720 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8730 case CA_SET_CE_SCORE:
8732 #if USE_NEW_CUSTOM_VALUE
8733 int last_ce_score = ei->collect_score;
8735 ei->collect_score = action_arg_number_new;
8738 printf("::: CE score == %d\n", ei->collect_score);
8741 if (ei->collect_score != last_ce_score)
8743 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8744 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8746 if (ei->collect_score == 0)
8751 printf("::: CE_SCORE_GETS_ZERO\n");
8754 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8755 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8758 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8763 This is a very special case that seems to be a mixture between
8764 CheckElementChange() and CheckTriggeredElementChange(): while
8765 the first one only affects single elements that are triggered
8766 directly, the second one affects multiple elements in the playfield
8767 that are triggered indirectly by another element. This is a third
8768 case: Changing the CE score always affects multiple identical CEs,
8769 so every affected CE must be checked, not only the single CE for
8770 which the CE score was changed in the first place (as every instance
8771 of that CE shares the same CE score, and therefore also can change)!
8773 SCAN_PLAYFIELD(xx, yy)
8775 if (Feld[xx][yy] == element)
8776 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8777 CE_SCORE_GETS_ZERO);
8788 /* ---------- engine actions ------------------------------------------ */
8790 case CA_SET_ENGINE_SCAN_MODE:
8792 InitPlayfieldScanMode(action_arg);
8802 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8804 int old_element = Feld[x][y];
8805 int new_element = get_element_from_group_element(element);
8806 int previous_move_direction = MovDir[x][y];
8807 #if USE_NEW_CUSTOM_VALUE
8808 int last_ce_value = CustomValue[x][y];
8810 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8811 boolean add_player_onto_element = (new_element_is_player &&
8812 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8813 /* this breaks SnakeBite when a snake is
8814 halfway through a door that closes */
8815 /* NOW FIXED AT LEVEL INIT IN files.c */
8816 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8818 IS_WALKABLE(old_element));
8821 /* check if element under the player changes from accessible to unaccessible
8822 (needed for special case of dropping element which then changes) */
8823 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8824 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8832 if (!add_player_onto_element)
8834 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8835 RemoveMovingField(x, y);
8839 Feld[x][y] = new_element;
8841 #if !USE_GFX_RESET_GFX_ANIMATION
8842 ResetGfxAnimation(x, y);
8843 ResetRandomAnimationValue(x, y);
8846 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8847 MovDir[x][y] = previous_move_direction;
8849 #if USE_NEW_CUSTOM_VALUE
8850 if (element_info[new_element].use_last_ce_value)
8851 CustomValue[x][y] = last_ce_value;
8854 InitField_WithBug1(x, y, FALSE);
8856 new_element = Feld[x][y]; /* element may have changed */
8858 #if USE_GFX_RESET_GFX_ANIMATION
8859 ResetGfxAnimation(x, y);
8860 ResetRandomAnimationValue(x, y);
8863 DrawLevelField(x, y);
8865 if (GFX_CRUMBLED(new_element))
8866 DrawLevelFieldCrumbledSandNeighbours(x, y);
8870 /* check if element under the player changes from accessible to unaccessible
8871 (needed for special case of dropping element which then changes) */
8872 /* (must be checked after creating new element for walkable group elements) */
8873 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8874 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8882 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8883 if (new_element_is_player)
8884 RelocatePlayer(x, y, new_element);
8887 ChangeCount[x][y]++; /* count number of changes in the same frame */
8889 TestIfBadThingTouchesPlayer(x, y);
8890 TestIfPlayerTouchesCustomElement(x, y);
8891 TestIfElementTouchesCustomElement(x, y);
8894 static void CreateField(int x, int y, int element)
8896 CreateFieldExt(x, y, element, FALSE);
8899 static void CreateElementFromChange(int x, int y, int element)
8901 element = GET_VALID_RUNTIME_ELEMENT(element);
8903 #if USE_STOP_CHANGED_ELEMENTS
8904 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8906 int old_element = Feld[x][y];
8908 /* prevent changed element from moving in same engine frame
8909 unless both old and new element can either fall or move */
8910 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8911 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8916 CreateFieldExt(x, y, element, TRUE);
8919 static boolean ChangeElement(int x, int y, int element, int page)
8921 struct ElementInfo *ei = &element_info[element];
8922 struct ElementChangeInfo *change = &ei->change_page[page];
8923 int ce_value = CustomValue[x][y];
8924 int ce_score = ei->collect_score;
8926 int old_element = Feld[x][y];
8928 /* always use default change event to prevent running into a loop */
8929 if (ChangeEvent[x][y] == -1)
8930 ChangeEvent[x][y] = CE_DELAY;
8932 if (ChangeEvent[x][y] == CE_DELAY)
8934 /* reset actual trigger element, trigger player and action element */
8935 change->actual_trigger_element = EL_EMPTY;
8936 change->actual_trigger_player = EL_PLAYER_1;
8937 change->actual_trigger_side = CH_SIDE_NONE;
8938 change->actual_trigger_ce_value = 0;
8939 change->actual_trigger_ce_score = 0;
8942 /* do not change elements more than a specified maximum number of changes */
8943 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8946 ChangeCount[x][y]++; /* count number of changes in the same frame */
8948 if (change->explode)
8955 if (change->use_target_content)
8957 boolean complete_replace = TRUE;
8958 boolean can_replace[3][3];
8961 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8964 boolean is_walkable;
8965 boolean is_diggable;
8966 boolean is_collectible;
8967 boolean is_removable;
8968 boolean is_destructible;
8969 int ex = x + xx - 1;
8970 int ey = y + yy - 1;
8971 int content_element = change->target_content.e[xx][yy];
8974 can_replace[xx][yy] = TRUE;
8976 if (ex == x && ey == y) /* do not check changing element itself */
8979 if (content_element == EL_EMPTY_SPACE)
8981 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8986 if (!IN_LEV_FIELD(ex, ey))
8988 can_replace[xx][yy] = FALSE;
8989 complete_replace = FALSE;
8996 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8997 e = MovingOrBlocked2Element(ex, ey);
8999 is_empty = (IS_FREE(ex, ey) ||
9000 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9002 is_walkable = (is_empty || IS_WALKABLE(e));
9003 is_diggable = (is_empty || IS_DIGGABLE(e));
9004 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9005 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9006 is_removable = (is_diggable || is_collectible);
9008 can_replace[xx][yy] =
9009 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9010 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9011 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9012 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9013 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9014 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9015 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9017 if (!can_replace[xx][yy])
9018 complete_replace = FALSE;
9021 if (!change->only_if_complete || complete_replace)
9023 boolean something_has_changed = FALSE;
9025 if (change->only_if_complete && change->use_random_replace &&
9026 RND(100) < change->random_percentage)
9029 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9031 int ex = x + xx - 1;
9032 int ey = y + yy - 1;
9033 int content_element;
9035 if (can_replace[xx][yy] && (!change->use_random_replace ||
9036 RND(100) < change->random_percentage))
9038 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9039 RemoveMovingField(ex, ey);
9041 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9043 content_element = change->target_content.e[xx][yy];
9044 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9045 ce_value, ce_score);
9047 CreateElementFromChange(ex, ey, target_element);
9049 something_has_changed = TRUE;
9051 /* for symmetry reasons, freeze newly created border elements */
9052 if (ex != x || ey != y)
9053 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9057 if (something_has_changed)
9059 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9060 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9066 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9067 ce_value, ce_score);
9069 if (element == EL_DIAGONAL_GROWING ||
9070 element == EL_DIAGONAL_SHRINKING)
9072 target_element = Store[x][y];
9074 Store[x][y] = EL_EMPTY;
9077 CreateElementFromChange(x, y, target_element);
9079 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9080 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9083 /* this uses direct change before indirect change */
9084 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9089 #if USE_NEW_DELAYED_ACTION
9091 static void HandleElementChange(int x, int y, int page)
9093 int element = MovingOrBlocked2Element(x, y);
9094 struct ElementInfo *ei = &element_info[element];
9095 struct ElementChangeInfo *change = &ei->change_page[page];
9098 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9099 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9102 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9103 x, y, element, element_info[element].token_name);
9104 printf("HandleElementChange(): This should never happen!\n");
9109 /* this can happen with classic bombs on walkable, changing elements */
9110 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9113 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9114 ChangeDelay[x][y] = 0;
9120 if (ChangeDelay[x][y] == 0) /* initialize element change */
9122 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9124 if (change->can_change)
9126 ResetGfxAnimation(x, y);
9127 ResetRandomAnimationValue(x, y);
9129 if (change->pre_change_function)
9130 change->pre_change_function(x, y);
9134 ChangeDelay[x][y]--;
9136 if (ChangeDelay[x][y] != 0) /* continue element change */
9138 if (change->can_change)
9140 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9142 if (IS_ANIMATED(graphic))
9143 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9145 if (change->change_function)
9146 change->change_function(x, y);
9149 else /* finish element change */
9151 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9153 page = ChangePage[x][y];
9154 ChangePage[x][y] = -1;
9156 change = &ei->change_page[page];
9159 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9161 ChangeDelay[x][y] = 1; /* try change after next move step */
9162 ChangePage[x][y] = page; /* remember page to use for change */
9167 if (change->can_change)
9169 if (ChangeElement(x, y, element, page))
9171 if (change->post_change_function)
9172 change->post_change_function(x, y);
9176 if (change->has_action)
9177 ExecuteCustomElementAction(x, y, element, page);
9183 static void HandleElementChange(int x, int y, int page)
9185 int element = MovingOrBlocked2Element(x, y);
9186 struct ElementInfo *ei = &element_info[element];
9187 struct ElementChangeInfo *change = &ei->change_page[page];
9190 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9193 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9194 x, y, element, element_info[element].token_name);
9195 printf("HandleElementChange(): This should never happen!\n");
9200 /* this can happen with classic bombs on walkable, changing elements */
9201 if (!CAN_CHANGE(element))
9204 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9205 ChangeDelay[x][y] = 0;
9211 if (ChangeDelay[x][y] == 0) /* initialize element change */
9213 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9215 ResetGfxAnimation(x, y);
9216 ResetRandomAnimationValue(x, y);
9218 if (change->pre_change_function)
9219 change->pre_change_function(x, y);
9222 ChangeDelay[x][y]--;
9224 if (ChangeDelay[x][y] != 0) /* continue element change */
9226 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9228 if (IS_ANIMATED(graphic))
9229 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9231 if (change->change_function)
9232 change->change_function(x, y);
9234 else /* finish element change */
9236 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9238 page = ChangePage[x][y];
9239 ChangePage[x][y] = -1;
9241 change = &ei->change_page[page];
9244 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9246 ChangeDelay[x][y] = 1; /* try change after next move step */
9247 ChangePage[x][y] = page; /* remember page to use for change */
9252 if (ChangeElement(x, y, element, page))
9254 if (change->post_change_function)
9255 change->post_change_function(x, y);
9262 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9263 int trigger_element,
9269 boolean change_done_any = FALSE;
9270 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9273 if (!(trigger_events[trigger_element][trigger_event]))
9276 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9278 int element = EL_CUSTOM_START + i;
9279 boolean change_done = FALSE;
9282 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9283 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9286 for (p = 0; p < element_info[element].num_change_pages; p++)
9288 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9290 if (change->can_change_or_has_action &&
9291 change->has_event[trigger_event] &&
9292 change->trigger_side & trigger_side &&
9293 change->trigger_player & trigger_player &&
9294 change->trigger_page & trigger_page_bits &&
9295 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9297 change->actual_trigger_element = trigger_element;
9298 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9299 change->actual_trigger_side = trigger_side;
9300 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9301 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9303 if ((change->can_change && !change_done) || change->has_action)
9308 SCAN_PLAYFIELD(x, y)
9310 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9313 if (Feld[x][y] == element)
9315 if (change->can_change && !change_done)
9317 ChangeDelay[x][y] = 1;
9318 ChangeEvent[x][y] = trigger_event;
9320 HandleElementChange(x, y, p);
9322 #if USE_NEW_DELAYED_ACTION
9323 else if (change->has_action)
9325 ExecuteCustomElementAction(x, y, element, p);
9326 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9329 if (change->has_action)
9331 ExecuteCustomElementAction(x, y, element, p);
9332 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9338 if (change->can_change)
9341 change_done_any = TRUE;
9348 return change_done_any;
9351 static boolean CheckElementChangeExt(int x, int y,
9353 int trigger_element,
9358 boolean change_done = FALSE;
9361 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9362 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9365 if (Feld[x][y] == EL_BLOCKED)
9367 Blocked2Moving(x, y, &x, &y);
9368 element = Feld[x][y];
9372 /* check if element has already changed */
9373 if (Feld[x][y] != element)
9376 /* check if element has already changed or is about to change after moving */
9377 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9378 Feld[x][y] != element) ||
9380 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9381 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9382 ChangePage[x][y] != -1)))
9386 for (p = 0; p < element_info[element].num_change_pages; p++)
9388 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9390 boolean check_trigger_element =
9391 (trigger_event == CE_TOUCHING_X ||
9392 trigger_event == CE_HITTING_X ||
9393 trigger_event == CE_HIT_BY_X);
9395 if (change->can_change_or_has_action &&
9396 change->has_event[trigger_event] &&
9397 change->trigger_side & trigger_side &&
9398 change->trigger_player & trigger_player &&
9399 (!check_trigger_element ||
9400 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9402 change->actual_trigger_element = trigger_element;
9403 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9404 change->actual_trigger_side = trigger_side;
9405 change->actual_trigger_ce_value = CustomValue[x][y];
9406 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9408 /* special case: trigger element not at (x,y) position for some events */
9409 if (check_trigger_element)
9421 { 0, 0 }, { 0, 0 }, { 0, 0 },
9425 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9426 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9428 change->actual_trigger_ce_value = CustomValue[xx][yy];
9429 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9432 if (change->can_change && !change_done)
9434 ChangeDelay[x][y] = 1;
9435 ChangeEvent[x][y] = trigger_event;
9437 HandleElementChange(x, y, p);
9441 #if USE_NEW_DELAYED_ACTION
9442 else if (change->has_action)
9444 ExecuteCustomElementAction(x, y, element, p);
9445 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9448 if (change->has_action)
9450 ExecuteCustomElementAction(x, y, element, p);
9451 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9460 static void PlayPlayerSound(struct PlayerInfo *player)
9462 int jx = player->jx, jy = player->jy;
9463 int sound_element = player->artwork_element;
9464 int last_action = player->last_action_waiting;
9465 int action = player->action_waiting;
9467 if (player->is_waiting)
9469 if (action != last_action)
9470 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9472 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9476 if (action != last_action)
9477 StopSound(element_info[sound_element].sound[last_action]);
9479 if (last_action == ACTION_SLEEPING)
9480 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9484 static void PlayAllPlayersSound()
9488 for (i = 0; i < MAX_PLAYERS; i++)
9489 if (stored_player[i].active)
9490 PlayPlayerSound(&stored_player[i]);
9493 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9495 boolean last_waiting = player->is_waiting;
9496 int move_dir = player->MovDir;
9498 player->dir_waiting = move_dir;
9499 player->last_action_waiting = player->action_waiting;
9503 if (!last_waiting) /* not waiting -> waiting */
9505 player->is_waiting = TRUE;
9507 player->frame_counter_bored =
9509 game.player_boring_delay_fixed +
9510 SimpleRND(game.player_boring_delay_random);
9511 player->frame_counter_sleeping =
9513 game.player_sleeping_delay_fixed +
9514 SimpleRND(game.player_sleeping_delay_random);
9517 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9519 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9523 if (game.player_sleeping_delay_fixed +
9524 game.player_sleeping_delay_random > 0 &&
9525 player->anim_delay_counter == 0 &&
9526 player->post_delay_counter == 0 &&
9527 FrameCounter >= player->frame_counter_sleeping)
9528 player->is_sleeping = TRUE;
9529 else if (game.player_boring_delay_fixed +
9530 game.player_boring_delay_random > 0 &&
9531 FrameCounter >= player->frame_counter_bored)
9532 player->is_bored = TRUE;
9534 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9535 player->is_bored ? ACTION_BORING :
9539 if (player->is_sleeping && player->use_murphy)
9541 /* special case for sleeping Murphy when leaning against non-free tile */
9543 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9544 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9545 !IS_MOVING(player->jx - 1, player->jy)))
9547 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9548 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9549 !IS_MOVING(player->jx + 1, player->jy)))
9550 move_dir = MV_RIGHT;
9552 player->is_sleeping = FALSE;
9554 player->dir_waiting = move_dir;
9558 if (player->is_sleeping)
9560 if (player->num_special_action_sleeping > 0)
9562 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9564 int last_special_action = player->special_action_sleeping;
9565 int num_special_action = player->num_special_action_sleeping;
9566 int special_action =
9567 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9568 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9569 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9570 last_special_action + 1 : ACTION_SLEEPING);
9571 int special_graphic =
9572 el_act_dir2img(player->artwork_element, special_action, move_dir);
9574 player->anim_delay_counter =
9575 graphic_info[special_graphic].anim_delay_fixed +
9576 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9577 player->post_delay_counter =
9578 graphic_info[special_graphic].post_delay_fixed +
9579 SimpleRND(graphic_info[special_graphic].post_delay_random);
9581 player->special_action_sleeping = special_action;
9584 if (player->anim_delay_counter > 0)
9586 player->action_waiting = player->special_action_sleeping;
9587 player->anim_delay_counter--;
9589 else if (player->post_delay_counter > 0)
9591 player->post_delay_counter--;
9595 else if (player->is_bored)
9597 if (player->num_special_action_bored > 0)
9599 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9601 int special_action =
9602 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9603 int special_graphic =
9604 el_act_dir2img(player->artwork_element, special_action, move_dir);
9606 player->anim_delay_counter =
9607 graphic_info[special_graphic].anim_delay_fixed +
9608 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9609 player->post_delay_counter =
9610 graphic_info[special_graphic].post_delay_fixed +
9611 SimpleRND(graphic_info[special_graphic].post_delay_random);
9613 player->special_action_bored = special_action;
9616 if (player->anim_delay_counter > 0)
9618 player->action_waiting = player->special_action_bored;
9619 player->anim_delay_counter--;
9621 else if (player->post_delay_counter > 0)
9623 player->post_delay_counter--;
9628 else if (last_waiting) /* waiting -> not waiting */
9630 player->is_waiting = FALSE;
9631 player->is_bored = FALSE;
9632 player->is_sleeping = FALSE;
9634 player->frame_counter_bored = -1;
9635 player->frame_counter_sleeping = -1;
9637 player->anim_delay_counter = 0;
9638 player->post_delay_counter = 0;
9640 player->dir_waiting = player->MovDir;
9641 player->action_waiting = ACTION_DEFAULT;
9643 player->special_action_bored = ACTION_DEFAULT;
9644 player->special_action_sleeping = ACTION_DEFAULT;
9648 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9650 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9651 int left = player_action & JOY_LEFT;
9652 int right = player_action & JOY_RIGHT;
9653 int up = player_action & JOY_UP;
9654 int down = player_action & JOY_DOWN;
9655 int button1 = player_action & JOY_BUTTON_1;
9656 int button2 = player_action & JOY_BUTTON_2;
9657 int dx = (left ? -1 : right ? 1 : 0);
9658 int dy = (up ? -1 : down ? 1 : 0);
9660 if (!player->active || tape.pausing)
9666 snapped = SnapField(player, dx, dy);
9670 dropped = DropElement(player);
9672 moved = MovePlayer(player, dx, dy);
9675 if (tape.single_step && tape.recording && !tape.pausing)
9677 if (button1 || (dropped && !moved))
9679 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9680 SnapField(player, 0, 0); /* stop snapping */
9684 SetPlayerWaiting(player, FALSE);
9686 return player_action;
9690 /* no actions for this player (no input at player's configured device) */
9692 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9693 SnapField(player, 0, 0);
9694 CheckGravityMovementWhenNotMoving(player);
9696 if (player->MovPos == 0)
9697 SetPlayerWaiting(player, TRUE);
9699 if (player->MovPos == 0) /* needed for tape.playing */
9700 player->is_moving = FALSE;
9702 player->is_dropping = FALSE;
9703 player->is_dropping_pressed = FALSE;
9704 player->drop_pressed_delay = 0;
9710 static void CheckLevelTime()
9714 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9716 if (level.native_em_level->lev->home == 0) /* all players at home */
9718 local_player->LevelSolved = TRUE;
9719 AllPlayersGone = TRUE;
9721 level.native_em_level->lev->home = -1;
9724 if (level.native_em_level->ply[0]->alive == 0 &&
9725 level.native_em_level->ply[1]->alive == 0 &&
9726 level.native_em_level->ply[2]->alive == 0 &&
9727 level.native_em_level->ply[3]->alive == 0) /* all dead */
9728 AllPlayersGone = TRUE;
9731 if (TimeFrames >= FRAMES_PER_SECOND)
9736 for (i = 0; i < MAX_PLAYERS; i++)
9738 struct PlayerInfo *player = &stored_player[i];
9740 if (SHIELD_ON(player))
9742 player->shield_normal_time_left--;
9744 if (player->shield_deadly_time_left > 0)
9745 player->shield_deadly_time_left--;
9749 if (!level.use_step_counter)
9757 if (TimeLeft <= 10 && setup.time_limit)
9758 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9760 DrawGameValue_Time(TimeLeft);
9762 if (!TimeLeft && setup.time_limit)
9764 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9765 level.native_em_level->lev->killed_out_of_time = TRUE;
9767 for (i = 0; i < MAX_PLAYERS; i++)
9768 KillPlayer(&stored_player[i]);
9771 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9772 DrawGameValue_Time(TimePlayed);
9774 level.native_em_level->lev->time =
9775 (level.time == 0 ? TimePlayed : TimeLeft);
9778 if (tape.recording || tape.playing)
9779 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9783 void AdvanceFrameAndPlayerCounters(int player_nr)
9788 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9789 FrameCounter, FrameCounter + 1);
9792 /* advance frame counters (global frame counter and time frame counter) */
9796 /* advance player counters (counters for move delay, move animation etc.) */
9797 for (i = 0; i < MAX_PLAYERS; i++)
9799 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9800 int move_delay_value = stored_player[i].move_delay_value;
9801 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9803 if (!advance_player_counters) /* not all players may be affected */
9806 #if USE_NEW_PLAYER_ANIM
9807 if (move_frames == 0) /* less than one move per game frame */
9809 int stepsize = TILEX / move_delay_value;
9810 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9811 int count = (stored_player[i].is_moving ?
9812 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9814 if (count % delay == 0)
9819 stored_player[i].Frame += move_frames;
9821 if (stored_player[i].MovPos != 0)
9822 stored_player[i].StepFrame += move_frames;
9824 if (stored_player[i].move_delay > 0)
9825 stored_player[i].move_delay--;
9827 /* due to bugs in previous versions, counter must count up, not down */
9828 if (stored_player[i].push_delay != -1)
9829 stored_player[i].push_delay++;
9831 if (stored_player[i].drop_delay > 0)
9832 stored_player[i].drop_delay--;
9834 if (stored_player[i].is_dropping_pressed)
9835 stored_player[i].drop_pressed_delay++;
9839 void StartGameActions(boolean init_network_game, boolean record_tape,
9842 unsigned long new_random_seed = InitRND(random_seed);
9845 TapeStartRecording(new_random_seed);
9847 #if defined(NETWORK_AVALIABLE)
9848 if (init_network_game)
9850 SendToServer_StartPlaying();
9861 static unsigned long game_frame_delay = 0;
9862 unsigned long game_frame_delay_value;
9863 byte *recorded_player_action;
9864 byte summarized_player_action = 0;
9865 byte tape_action[MAX_PLAYERS];
9868 if (game.restart_level)
9869 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9871 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9873 if (level.native_em_level->lev->home == 0) /* all players at home */
9875 local_player->LevelSolved = TRUE;
9876 AllPlayersGone = TRUE;
9878 level.native_em_level->lev->home = -1;
9881 if (level.native_em_level->ply[0]->alive == 0 &&
9882 level.native_em_level->ply[1]->alive == 0 &&
9883 level.native_em_level->ply[2]->alive == 0 &&
9884 level.native_em_level->ply[3]->alive == 0) /* all dead */
9885 AllPlayersGone = TRUE;
9888 if (local_player->LevelSolved)
9891 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9894 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9897 game_frame_delay_value =
9898 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9900 if (tape.playing && tape.warp_forward && !tape.pausing)
9901 game_frame_delay_value = 0;
9903 /* ---------- main game synchronization point ---------- */
9905 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9907 if (network_playing && !network_player_action_received)
9909 /* try to get network player actions in time */
9911 #if defined(NETWORK_AVALIABLE)
9912 /* last chance to get network player actions without main loop delay */
9916 /* game was quit by network peer */
9917 if (game_status != GAME_MODE_PLAYING)
9920 if (!network_player_action_received)
9921 return; /* failed to get network player actions in time */
9923 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9929 /* at this point we know that we really continue executing the game */
9932 network_player_action_received = FALSE;
9935 /* when playing tape, read previously recorded player input from tape data */
9936 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9939 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9947 if (recorded_player_action == NULL)
9948 printf("!!! THIS SHOULD NOT HAPPEN !!!\n");
9950 printf("::: %05d: TAPE PLAYING: %08x\n",
9951 FrameCounter, recorded_player_action[0]);
9955 if (tape.set_centered_player)
9957 game.centered_player_nr_next = tape.centered_player_nr_next;
9958 game.set_centered_player = TRUE;
9961 for (i = 0; i < MAX_PLAYERS; i++)
9963 summarized_player_action |= stored_player[i].action;
9965 if (!network_playing)
9966 stored_player[i].effective_action = stored_player[i].action;
9969 #if defined(NETWORK_AVALIABLE)
9970 if (network_playing)
9971 SendToServer_MovePlayer(summarized_player_action);
9974 if (!options.network && !setup.team_mode)
9975 local_player->effective_action = summarized_player_action;
9977 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9979 for (i = 0; i < MAX_PLAYERS; i++)
9980 stored_player[i].effective_action =
9981 (i == game.centered_player_nr ? summarized_player_action : 0);
9984 if (recorded_player_action != NULL)
9985 for (i = 0; i < MAX_PLAYERS; i++)
9986 stored_player[i].effective_action = recorded_player_action[i];
9988 for (i = 0; i < MAX_PLAYERS; i++)
9990 tape_action[i] = stored_player[i].effective_action;
9992 /* (this can only happen in the R'n'D game engine) */
9993 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9994 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9997 /* only record actions from input devices, but not programmed actions */
9999 TapeRecordAction(tape_action);
10002 if (tape.recording)
10003 printf("::: %05d: TAPE RECORDING: %08x\n",
10004 FrameCounter, tape_action[0]);
10007 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10009 GameActions_EM_Main();
10017 void GameActions_EM_Main()
10019 byte effective_action[MAX_PLAYERS];
10020 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10023 for (i = 0; i < MAX_PLAYERS; i++)
10024 effective_action[i] = stored_player[i].effective_action;
10027 printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
10030 GameActions_EM(effective_action, warp_mode);
10034 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10037 void GameActions_RND()
10039 int magic_wall_x = 0, magic_wall_y = 0;
10040 int i, x, y, element, graphic;
10042 InitPlayfieldScanModeVars();
10044 #if USE_ONE_MORE_CHANGE_PER_FRAME
10045 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10047 SCAN_PLAYFIELD(x, y)
10049 ChangeCount[x][y] = 0;
10050 ChangeEvent[x][y] = -1;
10056 if (game.set_centered_player)
10058 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10060 /* switching to "all players" only possible if all players fit to screen */
10061 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10063 game.centered_player_nr_next = game.centered_player_nr;
10064 game.set_centered_player = FALSE;
10067 /* do not switch focus to non-existing (or non-active) player */
10068 if (game.centered_player_nr_next >= 0 &&
10069 !stored_player[game.centered_player_nr_next].active)
10071 game.centered_player_nr_next = game.centered_player_nr;
10072 game.set_centered_player = FALSE;
10076 if (game.set_centered_player &&
10077 ScreenMovPos == 0) /* screen currently aligned at tile position */
10081 if (game.centered_player_nr_next == -1)
10083 setScreenCenteredToAllPlayers(&sx, &sy);
10087 sx = stored_player[game.centered_player_nr_next].jx;
10088 sy = stored_player[game.centered_player_nr_next].jy;
10091 game.centered_player_nr = game.centered_player_nr_next;
10092 game.set_centered_player = FALSE;
10094 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
10095 DrawGameDoorValues();
10099 for (i = 0; i < MAX_PLAYERS; i++)
10101 int actual_player_action = stored_player[i].effective_action;
10104 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10105 - rnd_equinox_tetrachloride 048
10106 - rnd_equinox_tetrachloride_ii 096
10107 - rnd_emanuel_schmieg 002
10108 - doctor_sloan_ww 001, 020
10110 if (stored_player[i].MovPos == 0)
10111 CheckGravityMovement(&stored_player[i]);
10114 /* overwrite programmed action with tape action */
10115 if (stored_player[i].programmed_action)
10116 actual_player_action = stored_player[i].programmed_action;
10119 PlayerActions(&stored_player[i], actual_player_action);
10121 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
10123 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10124 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10127 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10131 network_player_action_received = FALSE;
10134 ScrollScreen(NULL, SCROLL_GO_ON);
10136 /* for backwards compatibility, the following code emulates a fixed bug that
10137 occured when pushing elements (causing elements that just made their last
10138 pushing step to already (if possible) make their first falling step in the
10139 same game frame, which is bad); this code is also needed to use the famous
10140 "spring push bug" which is used in older levels and might be wanted to be
10141 used also in newer levels, but in this case the buggy pushing code is only
10142 affecting the "spring" element and no other elements */
10144 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10146 for (i = 0; i < MAX_PLAYERS; i++)
10148 struct PlayerInfo *player = &stored_player[i];
10149 int x = player->jx;
10150 int y = player->jy;
10152 if (player->active && player->is_pushing && player->is_moving &&
10154 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10155 Feld[x][y] == EL_SPRING))
10157 ContinueMoving(x, y);
10159 /* continue moving after pushing (this is actually a bug) */
10160 if (!IS_MOVING(x, y))
10162 Stop[x][y] = FALSE;
10169 SCAN_PLAYFIELD(x, y)
10171 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10174 ChangeCount[x][y] = 0;
10175 ChangeEvent[x][y] = -1;
10177 /* this must be handled before main playfield loop */
10178 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10181 if (MovDelay[x][y] <= 0)
10185 #if USE_NEW_SNAP_DELAY
10186 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10189 if (MovDelay[x][y] <= 0)
10192 DrawLevelField(x, y);
10194 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10200 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10202 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10203 printf("GameActions(): This should never happen!\n");
10205 ChangePage[x][y] = -1;
10209 Stop[x][y] = FALSE;
10210 if (WasJustMoving[x][y] > 0)
10211 WasJustMoving[x][y]--;
10212 if (WasJustFalling[x][y] > 0)
10213 WasJustFalling[x][y]--;
10214 if (CheckCollision[x][y] > 0)
10215 CheckCollision[x][y]--;
10219 /* reset finished pushing action (not done in ContinueMoving() to allow
10220 continuous pushing animation for elements with zero push delay) */
10221 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10223 ResetGfxAnimation(x, y);
10224 DrawLevelField(x, y);
10228 if (IS_BLOCKED(x, y))
10232 Blocked2Moving(x, y, &oldx, &oldy);
10233 if (!IS_MOVING(oldx, oldy))
10235 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10236 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10237 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10238 printf("GameActions(): This should never happen!\n");
10245 SCAN_PLAYFIELD(x, y)
10247 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10250 element = Feld[x][y];
10251 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10254 printf("::: %d,%d\n", x, y);
10256 if (element == EL_ROCK)
10257 printf("::: Yo man! Rocks can fall!\n");
10261 ResetGfxFrame(x, y, TRUE);
10263 if (graphic_info[graphic].anim_global_sync)
10264 GfxFrame[x][y] = FrameCounter;
10265 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
10267 int old_gfx_frame = GfxFrame[x][y];
10269 GfxFrame[x][y] = CustomValue[x][y];
10272 if (GfxFrame[x][y] != old_gfx_frame)
10274 DrawLevelGraphicAnimation(x, y, graphic);
10276 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
10278 int old_gfx_frame = GfxFrame[x][y];
10280 GfxFrame[x][y] = element_info[element].collect_score;
10283 if (GfxFrame[x][y] != old_gfx_frame)
10285 DrawLevelGraphicAnimation(x, y, graphic);
10287 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
10289 int old_gfx_frame = GfxFrame[x][y];
10291 GfxFrame[x][y] = ChangeDelay[x][y];
10294 if (GfxFrame[x][y] != old_gfx_frame)
10296 DrawLevelGraphicAnimation(x, y, graphic);
10300 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10301 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10302 ResetRandomAnimationValue(x, y);
10304 SetRandomAnimationValue(x, y);
10306 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10308 if (IS_INACTIVE(element))
10310 if (IS_ANIMATED(graphic))
10311 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10316 /* this may take place after moving, so 'element' may have changed */
10317 if (IS_CHANGING(x, y) &&
10318 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10320 int page = element_info[element].event_page_nr[CE_DELAY];
10322 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
10326 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10330 if (element == EL_CUSTOM_255)
10331 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10335 HandleElementChange(x, y, page);
10337 if (CAN_CHANGE(element))
10338 HandleElementChange(x, y, page);
10340 if (HAS_ACTION(element))
10341 ExecuteCustomElementAction(x, y, element, page);
10346 element = Feld[x][y];
10347 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10350 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10354 element = Feld[x][y];
10355 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10357 if (IS_ANIMATED(graphic) &&
10358 !IS_MOVING(x, y) &&
10360 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10362 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10363 EdelsteinFunkeln(x, y);
10365 else if ((element == EL_ACID ||
10366 element == EL_EXIT_OPEN ||
10367 element == EL_SP_EXIT_OPEN ||
10368 element == EL_SP_TERMINAL ||
10369 element == EL_SP_TERMINAL_ACTIVE ||
10370 element == EL_EXTRA_TIME ||
10371 element == EL_SHIELD_NORMAL ||
10372 element == EL_SHIELD_DEADLY) &&
10373 IS_ANIMATED(graphic))
10374 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10375 else if (IS_MOVING(x, y))
10376 ContinueMoving(x, y);
10377 else if (IS_ACTIVE_BOMB(element))
10378 CheckDynamite(x, y);
10379 else if (element == EL_AMOEBA_GROWING)
10380 AmoebeWaechst(x, y);
10381 else if (element == EL_AMOEBA_SHRINKING)
10382 AmoebaDisappearing(x, y);
10384 #if !USE_NEW_AMOEBA_CODE
10385 else if (IS_AMOEBALIVE(element))
10386 AmoebeAbleger(x, y);
10389 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10391 else if (element == EL_EXIT_CLOSED)
10393 else if (element == EL_SP_EXIT_CLOSED)
10395 else if (element == EL_EXPANDABLE_WALL_GROWING)
10396 MauerWaechst(x, y);
10397 else if (element == EL_EXPANDABLE_WALL ||
10398 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10399 element == EL_EXPANDABLE_WALL_VERTICAL ||
10400 element == EL_EXPANDABLE_WALL_ANY ||
10401 element == EL_BD_EXPANDABLE_WALL)
10402 MauerAbleger(x, y);
10403 else if (element == EL_FLAMES)
10404 CheckForDragon(x, y);
10405 else if (element == EL_EXPLOSION)
10406 ; /* drawing of correct explosion animation is handled separately */
10407 else if (element == EL_ELEMENT_SNAPPING ||
10408 element == EL_DIAGONAL_SHRINKING ||
10409 element == EL_DIAGONAL_GROWING)
10412 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10414 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10417 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10418 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10421 if (element == EL_CUSTOM_255 ||
10422 element == EL_CUSTOM_256)
10423 DrawLevelGraphicAnimation(x, y, graphic);
10426 if (IS_BELT_ACTIVE(element))
10427 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10429 if (game.magic_wall_active)
10431 int jx = local_player->jx, jy = local_player->jy;
10433 /* play the element sound at the position nearest to the player */
10434 if ((element == EL_MAGIC_WALL_FULL ||
10435 element == EL_MAGIC_WALL_ACTIVE ||
10436 element == EL_MAGIC_WALL_EMPTYING ||
10437 element == EL_BD_MAGIC_WALL_FULL ||
10438 element == EL_BD_MAGIC_WALL_ACTIVE ||
10439 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10440 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10448 #if USE_NEW_AMOEBA_CODE
10449 /* new experimental amoeba growth stuff */
10450 if (!(FrameCounter % 8))
10452 static unsigned long random = 1684108901;
10454 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10456 x = RND(lev_fieldx);
10457 y = RND(lev_fieldy);
10458 element = Feld[x][y];
10460 if (!IS_PLAYER(x,y) &&
10461 (element == EL_EMPTY ||
10462 CAN_GROW_INTO(element) ||
10463 element == EL_QUICKSAND_EMPTY ||
10464 element == EL_ACID_SPLASH_LEFT ||
10465 element == EL_ACID_SPLASH_RIGHT))
10467 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10468 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10469 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10470 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10471 Feld[x][y] = EL_AMOEBA_DROP;
10474 random = random * 129 + 1;
10480 if (game.explosions_delayed)
10483 game.explosions_delayed = FALSE;
10486 SCAN_PLAYFIELD(x, y)
10488 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10491 element = Feld[x][y];
10493 if (ExplodeField[x][y])
10494 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10495 else if (element == EL_EXPLOSION)
10496 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10498 ExplodeField[x][y] = EX_TYPE_NONE;
10501 game.explosions_delayed = TRUE;
10504 if (game.magic_wall_active)
10506 if (!(game.magic_wall_time_left % 4))
10508 int element = Feld[magic_wall_x][magic_wall_y];
10510 if (element == EL_BD_MAGIC_WALL_FULL ||
10511 element == EL_BD_MAGIC_WALL_ACTIVE ||
10512 element == EL_BD_MAGIC_WALL_EMPTYING)
10513 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10515 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10518 if (game.magic_wall_time_left > 0)
10520 game.magic_wall_time_left--;
10521 if (!game.magic_wall_time_left)
10524 SCAN_PLAYFIELD(x, y)
10526 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10529 element = Feld[x][y];
10531 if (element == EL_MAGIC_WALL_ACTIVE ||
10532 element == EL_MAGIC_WALL_FULL)
10534 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10535 DrawLevelField(x, y);
10537 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10538 element == EL_BD_MAGIC_WALL_FULL)
10540 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10541 DrawLevelField(x, y);
10545 game.magic_wall_active = FALSE;
10550 if (game.light_time_left > 0)
10552 game.light_time_left--;
10554 if (game.light_time_left == 0)
10555 RedrawAllLightSwitchesAndInvisibleElements();
10558 if (game.timegate_time_left > 0)
10560 game.timegate_time_left--;
10562 if (game.timegate_time_left == 0)
10563 CloseAllOpenTimegates();
10566 if (game.lenses_time_left > 0)
10568 game.lenses_time_left--;
10570 if (game.lenses_time_left == 0)
10571 RedrawAllInvisibleElementsForLenses();
10574 if (game.magnify_time_left > 0)
10576 game.magnify_time_left--;
10578 if (game.magnify_time_left == 0)
10579 RedrawAllInvisibleElementsForMagnifier();
10582 for (i = 0; i < MAX_PLAYERS; i++)
10584 struct PlayerInfo *player = &stored_player[i];
10586 if (SHIELD_ON(player))
10588 if (player->shield_deadly_time_left)
10589 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10590 else if (player->shield_normal_time_left)
10591 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10598 PlayAllPlayersSound();
10600 if (options.debug) /* calculate frames per second */
10602 static unsigned long fps_counter = 0;
10603 static int fps_frames = 0;
10604 unsigned long fps_delay_ms = Counter() - fps_counter;
10608 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10610 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10613 fps_counter = Counter();
10616 redraw_mask |= REDRAW_FPS;
10619 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10621 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10623 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10625 local_player->show_envelope = 0;
10628 /* use random number generator in every frame to make it less predictable */
10629 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10633 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10635 int min_x = x, min_y = y, max_x = x, max_y = y;
10638 for (i = 0; i < MAX_PLAYERS; i++)
10640 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10642 if (!stored_player[i].active || &stored_player[i] == player)
10645 min_x = MIN(min_x, jx);
10646 min_y = MIN(min_y, jy);
10647 max_x = MAX(max_x, jx);
10648 max_y = MAX(max_y, jy);
10651 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10654 static boolean AllPlayersInVisibleScreen()
10658 for (i = 0; i < MAX_PLAYERS; i++)
10660 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10662 if (!stored_player[i].active)
10665 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10672 void ScrollLevel(int dx, int dy)
10674 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10677 BlitBitmap(drawto_field, drawto_field,
10678 FX + TILEX * (dx == -1) - softscroll_offset,
10679 FY + TILEY * (dy == -1) - softscroll_offset,
10680 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10681 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10682 FX + TILEX * (dx == 1) - softscroll_offset,
10683 FY + TILEY * (dy == 1) - softscroll_offset);
10687 x = (dx == 1 ? BX1 : BX2);
10688 for (y = BY1; y <= BY2; y++)
10689 DrawScreenField(x, y);
10694 y = (dy == 1 ? BY1 : BY2);
10695 for (x = BX1; x <= BX2; x++)
10696 DrawScreenField(x, y);
10699 redraw_mask |= REDRAW_FIELD;
10702 static boolean canFallDown(struct PlayerInfo *player)
10704 int jx = player->jx, jy = player->jy;
10706 return (IN_LEV_FIELD(jx, jy + 1) &&
10707 (IS_FREE(jx, jy + 1) ||
10708 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10709 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10710 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10713 static boolean canPassField(int x, int y, int move_dir)
10715 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10716 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10717 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10718 int nextx = x + dx;
10719 int nexty = y + dy;
10720 int element = Feld[x][y];
10722 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10723 !CAN_MOVE(element) &&
10724 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10725 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10726 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10729 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10731 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10732 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10733 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10737 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10738 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10739 (IS_DIGGABLE(Feld[newx][newy]) ||
10740 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10741 canPassField(newx, newy, move_dir)));
10744 static void CheckGravityMovement(struct PlayerInfo *player)
10746 #if USE_PLAYER_GRAVITY
10747 if (player->gravity && !player->programmed_action)
10749 if (game.gravity && !player->programmed_action)
10752 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10753 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10754 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10755 int jx = player->jx, jy = player->jy;
10756 boolean player_is_moving_to_valid_field =
10757 (!player_is_snapping &&
10758 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10759 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10760 boolean player_can_fall_down = canFallDown(player);
10762 if (player_can_fall_down &&
10763 !player_is_moving_to_valid_field)
10764 player->programmed_action = MV_DOWN;
10768 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10770 return CheckGravityMovement(player);
10772 #if USE_PLAYER_GRAVITY
10773 if (player->gravity && !player->programmed_action)
10775 if (game.gravity && !player->programmed_action)
10778 int jx = player->jx, jy = player->jy;
10779 boolean field_under_player_is_free =
10780 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10781 boolean player_is_standing_on_valid_field =
10782 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10783 (IS_WALKABLE(Feld[jx][jy]) &&
10784 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10786 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10787 player->programmed_action = MV_DOWN;
10792 MovePlayerOneStep()
10793 -----------------------------------------------------------------------------
10794 dx, dy: direction (non-diagonal) to try to move the player to
10795 real_dx, real_dy: direction as read from input device (can be diagonal)
10798 boolean MovePlayerOneStep(struct PlayerInfo *player,
10799 int dx, int dy, int real_dx, int real_dy)
10801 int jx = player->jx, jy = player->jy;
10802 int new_jx = jx + dx, new_jy = jy + dy;
10803 #if !USE_FIXED_DONT_RUN_INTO
10807 boolean player_can_move = !player->cannot_move;
10809 if (!player->active || (!dx && !dy))
10810 return MP_NO_ACTION;
10812 player->MovDir = (dx < 0 ? MV_LEFT :
10813 dx > 0 ? MV_RIGHT :
10815 dy > 0 ? MV_DOWN : MV_NONE);
10817 if (!IN_LEV_FIELD(new_jx, new_jy))
10818 return MP_NO_ACTION;
10820 if (!player_can_move)
10823 if (player->MovPos == 0)
10825 player->is_moving = FALSE;
10826 player->is_digging = FALSE;
10827 player->is_collecting = FALSE;
10828 player->is_snapping = FALSE;
10829 player->is_pushing = FALSE;
10832 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10833 SnapField(player, 0, 0);
10837 return MP_NO_ACTION;
10842 if (!options.network && game.centered_player_nr == -1 &&
10843 !AllPlayersInSight(player, new_jx, new_jy))
10844 return MP_NO_ACTION;
10846 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10847 return MP_NO_ACTION;
10850 #if !USE_FIXED_DONT_RUN_INTO
10851 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10853 /* (moved to DigField()) */
10854 if (player_can_move && DONT_RUN_INTO(element))
10856 if (element == EL_ACID && dx == 0 && dy == 1)
10858 SplashAcid(new_jx, new_jy);
10859 Feld[jx][jy] = EL_PLAYER_1;
10860 InitMovingField(jx, jy, MV_DOWN);
10861 Store[jx][jy] = EL_ACID;
10862 ContinueMoving(jx, jy);
10863 BuryPlayer(player);
10866 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10872 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10874 #if USE_FIXED_DONT_RUN_INTO
10875 if (can_move == MP_DONT_RUN_INTO)
10879 if (can_move != MP_MOVING)
10882 #if USE_FIXED_DONT_RUN_INTO
10885 /* check if DigField() has caused relocation of the player */
10886 if (player->jx != jx || player->jy != jy)
10887 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10889 StorePlayer[jx][jy] = 0;
10890 player->last_jx = jx;
10891 player->last_jy = jy;
10892 player->jx = new_jx;
10893 player->jy = new_jy;
10894 StorePlayer[new_jx][new_jy] = player->element_nr;
10896 if (player->move_delay_value_next != -1)
10898 player->move_delay_value = player->move_delay_value_next;
10899 player->move_delay_value_next = -1;
10903 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10905 player->step_counter++;
10907 PlayerVisit[jx][jy] = FrameCounter;
10909 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10910 player->is_moving = TRUE;
10914 /* should better be called in MovePlayer(), but this breaks some tapes */
10915 ScrollPlayer(player, SCROLL_INIT);
10921 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10923 int jx = player->jx, jy = player->jy;
10924 int old_jx = jx, old_jy = jy;
10925 int moved = MP_NO_ACTION;
10927 if (!player->active)
10932 if (player->MovPos == 0)
10934 player->is_moving = FALSE;
10935 player->is_digging = FALSE;
10936 player->is_collecting = FALSE;
10937 player->is_snapping = FALSE;
10938 player->is_pushing = FALSE;
10944 if (player->move_delay > 0)
10947 player->move_delay = -1; /* set to "uninitialized" value */
10949 /* store if player is automatically moved to next field */
10950 player->is_auto_moving = (player->programmed_action != MV_NONE);
10952 /* remove the last programmed player action */
10953 player->programmed_action = 0;
10955 if (player->MovPos)
10957 /* should only happen if pre-1.2 tape recordings are played */
10958 /* this is only for backward compatibility */
10960 int original_move_delay_value = player->move_delay_value;
10963 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10967 /* scroll remaining steps with finest movement resolution */
10968 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10970 while (player->MovPos)
10972 ScrollPlayer(player, SCROLL_GO_ON);
10973 ScrollScreen(NULL, SCROLL_GO_ON);
10975 AdvanceFrameAndPlayerCounters(player->index_nr);
10981 player->move_delay_value = original_move_delay_value;
10984 player->is_active = FALSE;
10986 if (player->last_move_dir & MV_HORIZONTAL)
10988 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10989 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10993 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10994 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10997 #if USE_FIXED_BORDER_RUNNING_GFX
10998 if (!moved && !player->is_active)
11000 player->is_moving = FALSE;
11001 player->is_digging = FALSE;
11002 player->is_collecting = FALSE;
11003 player->is_snapping = FALSE;
11004 player->is_pushing = FALSE;
11012 if (moved & MP_MOVING && !ScreenMovPos &&
11013 (player->index_nr == game.centered_player_nr ||
11014 game.centered_player_nr == -1))
11016 if (moved & MP_MOVING && !ScreenMovPos &&
11017 (player == local_player || !options.network))
11020 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11021 int offset = (setup.scroll_delay ? 3 : 0);
11023 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11025 /* actual player has left the screen -- scroll in that direction */
11026 if (jx != old_jx) /* player has moved horizontally */
11027 scroll_x += (jx - old_jx);
11028 else /* player has moved vertically */
11029 scroll_y += (jy - old_jy);
11033 if (jx != old_jx) /* player has moved horizontally */
11035 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11036 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11037 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11039 /* don't scroll over playfield boundaries */
11040 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11041 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11043 /* don't scroll more than one field at a time */
11044 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11046 /* don't scroll against the player's moving direction */
11047 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11048 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11049 scroll_x = old_scroll_x;
11051 else /* player has moved vertically */
11053 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11054 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11055 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11057 /* don't scroll over playfield boundaries */
11058 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11059 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11061 /* don't scroll more than one field at a time */
11062 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11064 /* don't scroll against the player's moving direction */
11065 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11066 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11067 scroll_y = old_scroll_y;
11071 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11074 if (!options.network && game.centered_player_nr == -1 &&
11075 !AllPlayersInVisibleScreen())
11077 scroll_x = old_scroll_x;
11078 scroll_y = old_scroll_y;
11082 if (!options.network && !AllPlayersInVisibleScreen())
11084 scroll_x = old_scroll_x;
11085 scroll_y = old_scroll_y;
11090 ScrollScreen(player, SCROLL_INIT);
11091 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11096 player->StepFrame = 0;
11098 if (moved & MP_MOVING)
11100 if (old_jx != jx && old_jy == jy)
11101 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11102 else if (old_jx == jx && old_jy != jy)
11103 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11105 DrawLevelField(jx, jy); /* for "crumbled sand" */
11107 player->last_move_dir = player->MovDir;
11108 player->is_moving = TRUE;
11109 player->is_snapping = FALSE;
11110 player->is_switching = FALSE;
11111 player->is_dropping = FALSE;
11112 player->is_dropping_pressed = FALSE;
11113 player->drop_pressed_delay = 0;
11116 /* should better be called here than above, but this breaks some tapes */
11117 ScrollPlayer(player, SCROLL_INIT);
11122 CheckGravityMovementWhenNotMoving(player);
11124 player->is_moving = FALSE;
11126 /* at this point, the player is allowed to move, but cannot move right now
11127 (e.g. because of something blocking the way) -- ensure that the player
11128 is also allowed to move in the next frame (in old versions before 3.1.1,
11129 the player was forced to wait again for eight frames before next try) */
11131 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11132 player->move_delay = 0; /* allow direct movement in the next frame */
11135 if (player->move_delay == -1) /* not yet initialized by DigField() */
11136 player->move_delay = player->move_delay_value;
11138 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11140 TestIfPlayerTouchesBadThing(jx, jy);
11141 TestIfPlayerTouchesCustomElement(jx, jy);
11144 if (!player->active)
11145 RemovePlayer(player);
11150 void ScrollPlayer(struct PlayerInfo *player, int mode)
11152 int jx = player->jx, jy = player->jy;
11153 int last_jx = player->last_jx, last_jy = player->last_jy;
11154 int move_stepsize = TILEX / player->move_delay_value;
11156 #if USE_NEW_PLAYER_SPEED
11157 if (!player->active)
11160 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11163 if (!player->active || player->MovPos == 0)
11167 if (mode == SCROLL_INIT)
11169 player->actual_frame_counter = FrameCounter;
11170 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11172 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11173 Feld[last_jx][last_jy] == EL_EMPTY)
11175 int last_field_block_delay = 0; /* start with no blocking at all */
11176 int block_delay_adjustment = player->block_delay_adjustment;
11178 /* if player blocks last field, add delay for exactly one move */
11179 if (player->block_last_field)
11181 last_field_block_delay += player->move_delay_value;
11183 /* when blocking enabled, prevent moving up despite gravity */
11184 #if USE_PLAYER_GRAVITY
11185 if (player->gravity && player->MovDir == MV_UP)
11186 block_delay_adjustment = -1;
11188 if (game.gravity && player->MovDir == MV_UP)
11189 block_delay_adjustment = -1;
11193 /* add block delay adjustment (also possible when not blocking) */
11194 last_field_block_delay += block_delay_adjustment;
11196 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11197 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11200 #if USE_NEW_PLAYER_SPEED
11201 if (player->MovPos != 0) /* player has not yet reached destination */
11207 else if (!FrameReached(&player->actual_frame_counter, 1))
11211 printf("::: player->MovPos: %d -> %d\n",
11213 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
11216 #if USE_NEW_PLAYER_SPEED
11217 if (player->MovPos != 0)
11219 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11220 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11222 /* before DrawPlayer() to draw correct player graphic for this case */
11223 if (player->MovPos == 0)
11224 CheckGravityMovement(player);
11227 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11228 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11230 /* before DrawPlayer() to draw correct player graphic for this case */
11231 if (player->MovPos == 0)
11232 CheckGravityMovement(player);
11235 if (player->MovPos == 0) /* player reached destination field */
11238 printf("::: player reached destination field\n");
11241 if (player->move_delay_reset_counter > 0)
11243 player->move_delay_reset_counter--;
11245 if (player->move_delay_reset_counter == 0)
11247 /* continue with normal speed after quickly moving through gate */
11248 HALVE_PLAYER_SPEED(player);
11250 /* be able to make the next move without delay */
11251 player->move_delay = 0;
11255 player->last_jx = jx;
11256 player->last_jy = jy;
11258 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11259 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11260 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11262 DrawPlayer(player); /* needed here only to cleanup last field */
11263 RemovePlayer(player);
11265 if (local_player->friends_still_needed == 0 ||
11266 IS_SP_ELEMENT(Feld[jx][jy]))
11267 player->LevelSolved = player->GameOver = TRUE;
11270 /* this breaks one level: "machine", level 000 */
11272 int move_direction = player->MovDir;
11273 int enter_side = MV_DIR_OPPOSITE(move_direction);
11274 int leave_side = move_direction;
11275 int old_jx = last_jx;
11276 int old_jy = last_jy;
11277 int old_element = Feld[old_jx][old_jy];
11278 int new_element = Feld[jx][jy];
11280 if (IS_CUSTOM_ELEMENT(old_element))
11281 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11283 player->index_bit, leave_side);
11285 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11286 CE_PLAYER_LEAVES_X,
11287 player->index_bit, leave_side);
11289 if (IS_CUSTOM_ELEMENT(new_element))
11290 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11291 player->index_bit, enter_side);
11293 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11294 CE_PLAYER_ENTERS_X,
11295 player->index_bit, enter_side);
11297 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11298 CE_MOVE_OF_X, move_direction);
11301 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11303 TestIfPlayerTouchesBadThing(jx, jy);
11304 TestIfPlayerTouchesCustomElement(jx, jy);
11306 /* needed because pushed element has not yet reached its destination,
11307 so it would trigger a change event at its previous field location */
11308 if (!player->is_pushing)
11309 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11311 if (!player->active)
11312 RemovePlayer(player);
11315 if (level.use_step_counter)
11325 if (TimeLeft <= 10 && setup.time_limit)
11326 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11328 DrawGameValue_Time(TimeLeft);
11330 if (!TimeLeft && setup.time_limit)
11331 for (i = 0; i < MAX_PLAYERS; i++)
11332 KillPlayer(&stored_player[i]);
11334 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11335 DrawGameValue_Time(TimePlayed);
11338 if (tape.single_step && tape.recording && !tape.pausing &&
11339 !player->programmed_action)
11340 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11344 void ScrollScreen(struct PlayerInfo *player, int mode)
11346 static unsigned long screen_frame_counter = 0;
11348 if (mode == SCROLL_INIT)
11350 /* set scrolling step size according to actual player's moving speed */
11351 ScrollStepSize = TILEX / player->move_delay_value;
11353 screen_frame_counter = FrameCounter;
11354 ScreenMovDir = player->MovDir;
11355 ScreenMovPos = player->MovPos;
11356 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11359 else if (!FrameReached(&screen_frame_counter, 1))
11364 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11365 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11366 redraw_mask |= REDRAW_FIELD;
11369 ScreenMovDir = MV_NONE;
11372 void TestIfPlayerTouchesCustomElement(int x, int y)
11374 static int xy[4][2] =
11381 static int trigger_sides[4][2] =
11383 /* center side border side */
11384 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11385 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11386 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11387 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11389 static int touch_dir[4] =
11391 MV_LEFT | MV_RIGHT,
11396 int center_element = Feld[x][y]; /* should always be non-moving! */
11399 for (i = 0; i < NUM_DIRECTIONS; i++)
11401 int xx = x + xy[i][0];
11402 int yy = y + xy[i][1];
11403 int center_side = trigger_sides[i][0];
11404 int border_side = trigger_sides[i][1];
11405 int border_element;
11407 if (!IN_LEV_FIELD(xx, yy))
11410 if (IS_PLAYER(x, y))
11412 struct PlayerInfo *player = PLAYERINFO(x, y);
11414 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11415 border_element = Feld[xx][yy]; /* may be moving! */
11416 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11417 border_element = Feld[xx][yy];
11418 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11419 border_element = MovingOrBlocked2Element(xx, yy);
11421 continue; /* center and border element do not touch */
11423 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11424 player->index_bit, border_side);
11425 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11426 CE_PLAYER_TOUCHES_X,
11427 player->index_bit, border_side);
11429 else if (IS_PLAYER(xx, yy))
11431 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11433 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11435 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11436 continue; /* center and border element do not touch */
11439 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11440 player->index_bit, center_side);
11441 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11442 CE_PLAYER_TOUCHES_X,
11443 player->index_bit, center_side);
11449 #if USE_ELEMENT_TOUCHING_BUGFIX
11451 void TestIfElementTouchesCustomElement(int x, int y)
11453 static int xy[4][2] =
11460 static int trigger_sides[4][2] =
11462 /* center side border side */
11463 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11464 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11465 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11466 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11468 static int touch_dir[4] =
11470 MV_LEFT | MV_RIGHT,
11475 boolean change_center_element = FALSE;
11476 int center_element = Feld[x][y]; /* should always be non-moving! */
11477 int border_element_old[NUM_DIRECTIONS];
11480 for (i = 0; i < NUM_DIRECTIONS; i++)
11482 int xx = x + xy[i][0];
11483 int yy = y + xy[i][1];
11484 int border_element;
11486 border_element_old[i] = -1;
11488 if (!IN_LEV_FIELD(xx, yy))
11491 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11492 border_element = Feld[xx][yy]; /* may be moving! */
11493 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11494 border_element = Feld[xx][yy];
11495 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11496 border_element = MovingOrBlocked2Element(xx, yy);
11498 continue; /* center and border element do not touch */
11500 border_element_old[i] = border_element;
11503 for (i = 0; i < NUM_DIRECTIONS; i++)
11505 int xx = x + xy[i][0];
11506 int yy = y + xy[i][1];
11507 int center_side = trigger_sides[i][0];
11508 int border_element = border_element_old[i];
11510 if (border_element == -1)
11513 /* check for change of border element */
11514 CheckElementChangeBySide(xx, yy, border_element, center_element,
11515 CE_TOUCHING_X, center_side);
11518 for (i = 0; i < NUM_DIRECTIONS; i++)
11520 int border_side = trigger_sides[i][1];
11521 int border_element = border_element_old[i];
11523 if (border_element == -1)
11526 /* check for change of center element (but change it only once) */
11527 if (!change_center_element)
11528 change_center_element =
11529 CheckElementChangeBySide(x, y, center_element, border_element,
11530 CE_TOUCHING_X, border_side);
11536 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11538 static int xy[4][2] =
11545 static int trigger_sides[4][2] =
11547 /* center side border side */
11548 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11549 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11550 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11551 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11553 static int touch_dir[4] =
11555 MV_LEFT | MV_RIGHT,
11560 boolean change_center_element = FALSE;
11561 int center_element = Feld[x][y]; /* should always be non-moving! */
11564 for (i = 0; i < NUM_DIRECTIONS; i++)
11566 int xx = x + xy[i][0];
11567 int yy = y + xy[i][1];
11568 int center_side = trigger_sides[i][0];
11569 int border_side = trigger_sides[i][1];
11570 int border_element;
11572 if (!IN_LEV_FIELD(xx, yy))
11575 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11576 border_element = Feld[xx][yy]; /* may be moving! */
11577 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11578 border_element = Feld[xx][yy];
11579 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11580 border_element = MovingOrBlocked2Element(xx, yy);
11582 continue; /* center and border element do not touch */
11584 /* check for change of center element (but change it only once) */
11585 if (!change_center_element)
11586 change_center_element =
11587 CheckElementChangeBySide(x, y, center_element, border_element,
11588 CE_TOUCHING_X, border_side);
11590 /* check for change of border element */
11591 CheckElementChangeBySide(xx, yy, border_element, center_element,
11592 CE_TOUCHING_X, center_side);
11598 void TestIfElementHitsCustomElement(int x, int y, int direction)
11600 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11601 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11602 int hitx = x + dx, hity = y + dy;
11603 int hitting_element = Feld[x][y];
11604 int touched_element;
11606 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11609 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11610 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11612 if (IN_LEV_FIELD(hitx, hity))
11614 int opposite_direction = MV_DIR_OPPOSITE(direction);
11615 int hitting_side = direction;
11616 int touched_side = opposite_direction;
11617 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11618 MovDir[hitx][hity] != direction ||
11619 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11625 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11626 CE_HITTING_X, touched_side);
11628 CheckElementChangeBySide(hitx, hity, touched_element,
11629 hitting_element, CE_HIT_BY_X, hitting_side);
11631 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11632 CE_HIT_BY_SOMETHING, opposite_direction);
11636 /* "hitting something" is also true when hitting the playfield border */
11637 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11638 CE_HITTING_SOMETHING, direction);
11642 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11644 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11645 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11646 int hitx = x + dx, hity = y + dy;
11647 int hitting_element = Feld[x][y];
11648 int touched_element;
11650 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11651 !IS_FREE(hitx, hity) &&
11652 (!IS_MOVING(hitx, hity) ||
11653 MovDir[hitx][hity] != direction ||
11654 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11657 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11661 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11665 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11666 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11668 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11669 EP_CAN_SMASH_EVERYTHING, direction);
11671 if (IN_LEV_FIELD(hitx, hity))
11673 int opposite_direction = MV_DIR_OPPOSITE(direction);
11674 int hitting_side = direction;
11675 int touched_side = opposite_direction;
11677 int touched_element = MovingOrBlocked2Element(hitx, hity);
11680 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11681 MovDir[hitx][hity] != direction ||
11682 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11691 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11692 CE_SMASHED_BY_SOMETHING, opposite_direction);
11694 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11695 CE_OTHER_IS_SMASHING, touched_side);
11697 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11698 CE_OTHER_GETS_SMASHED, hitting_side);
11704 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11706 int i, kill_x = -1, kill_y = -1;
11708 int bad_element = -1;
11709 static int test_xy[4][2] =
11716 static int test_dir[4] =
11724 for (i = 0; i < NUM_DIRECTIONS; i++)
11726 int test_x, test_y, test_move_dir, test_element;
11728 test_x = good_x + test_xy[i][0];
11729 test_y = good_y + test_xy[i][1];
11731 if (!IN_LEV_FIELD(test_x, test_y))
11735 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11737 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11739 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11740 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11742 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11743 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11747 bad_element = test_element;
11753 if (kill_x != -1 || kill_y != -1)
11755 if (IS_PLAYER(good_x, good_y))
11757 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11759 if (player->shield_deadly_time_left > 0 &&
11760 !IS_INDESTRUCTIBLE(bad_element))
11761 Bang(kill_x, kill_y);
11762 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11763 KillPlayer(player);
11766 Bang(good_x, good_y);
11770 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11772 int i, kill_x = -1, kill_y = -1;
11773 int bad_element = Feld[bad_x][bad_y];
11774 static int test_xy[4][2] =
11781 static int touch_dir[4] =
11783 MV_LEFT | MV_RIGHT,
11788 static int test_dir[4] =
11796 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11799 for (i = 0; i < NUM_DIRECTIONS; i++)
11801 int test_x, test_y, test_move_dir, test_element;
11803 test_x = bad_x + test_xy[i][0];
11804 test_y = bad_y + test_xy[i][1];
11805 if (!IN_LEV_FIELD(test_x, test_y))
11809 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11811 test_element = Feld[test_x][test_y];
11813 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11814 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11816 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11817 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11819 /* good thing is player or penguin that does not move away */
11820 if (IS_PLAYER(test_x, test_y))
11822 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11824 if (bad_element == EL_ROBOT && player->is_moving)
11825 continue; /* robot does not kill player if he is moving */
11827 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11829 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11830 continue; /* center and border element do not touch */
11837 else if (test_element == EL_PENGUIN)
11846 if (kill_x != -1 || kill_y != -1)
11848 if (IS_PLAYER(kill_x, kill_y))
11850 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11852 if (player->shield_deadly_time_left > 0 &&
11853 !IS_INDESTRUCTIBLE(bad_element))
11854 Bang(bad_x, bad_y);
11855 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11856 KillPlayer(player);
11859 Bang(kill_x, kill_y);
11863 void TestIfPlayerTouchesBadThing(int x, int y)
11865 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11868 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11870 TestIfGoodThingHitsBadThing(x, y, move_dir);
11873 void TestIfBadThingTouchesPlayer(int x, int y)
11875 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11878 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11880 TestIfBadThingHitsGoodThing(x, y, move_dir);
11883 void TestIfFriendTouchesBadThing(int x, int y)
11885 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11888 void TestIfBadThingTouchesFriend(int x, int y)
11890 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11893 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11895 int i, kill_x = bad_x, kill_y = bad_y;
11896 static int xy[4][2] =
11904 for (i = 0; i < NUM_DIRECTIONS; i++)
11908 x = bad_x + xy[i][0];
11909 y = bad_y + xy[i][1];
11910 if (!IN_LEV_FIELD(x, y))
11913 element = Feld[x][y];
11914 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11915 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11923 if (kill_x != bad_x || kill_y != bad_y)
11924 Bang(bad_x, bad_y);
11927 void KillPlayer(struct PlayerInfo *player)
11929 int jx = player->jx, jy = player->jy;
11931 if (!player->active)
11934 /* remove accessible field at the player's position */
11935 Feld[jx][jy] = EL_EMPTY;
11937 /* deactivate shield (else Bang()/Explode() would not work right) */
11938 player->shield_normal_time_left = 0;
11939 player->shield_deadly_time_left = 0;
11942 BuryPlayer(player);
11945 static void KillPlayerUnlessEnemyProtected(int x, int y)
11947 if (!PLAYER_ENEMY_PROTECTED(x, y))
11948 KillPlayer(PLAYERINFO(x, y));
11951 static void KillPlayerUnlessExplosionProtected(int x, int y)
11953 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11954 KillPlayer(PLAYERINFO(x, y));
11957 void BuryPlayer(struct PlayerInfo *player)
11959 int jx = player->jx, jy = player->jy;
11961 if (!player->active)
11964 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11965 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11967 player->GameOver = TRUE;
11968 RemovePlayer(player);
11971 void RemovePlayer(struct PlayerInfo *player)
11973 int jx = player->jx, jy = player->jy;
11974 int i, found = FALSE;
11976 player->present = FALSE;
11977 player->active = FALSE;
11979 if (!ExplodeField[jx][jy])
11980 StorePlayer[jx][jy] = 0;
11982 if (player->is_moving)
11983 DrawLevelField(player->last_jx, player->last_jy);
11985 for (i = 0; i < MAX_PLAYERS; i++)
11986 if (stored_player[i].active)
11990 AllPlayersGone = TRUE;
11996 #if USE_NEW_SNAP_DELAY
11997 static void setFieldForSnapping(int x, int y, int element, int direction)
11999 struct ElementInfo *ei = &element_info[element];
12000 int direction_bit = MV_DIR_TO_BIT(direction);
12001 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12002 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12003 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12005 Feld[x][y] = EL_ELEMENT_SNAPPING;
12006 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12008 ResetGfxAnimation(x, y);
12010 GfxElement[x][y] = element;
12011 GfxAction[x][y] = action;
12012 GfxDir[x][y] = direction;
12013 GfxFrame[x][y] = -1;
12018 =============================================================================
12019 checkDiagonalPushing()
12020 -----------------------------------------------------------------------------
12021 check if diagonal input device direction results in pushing of object
12022 (by checking if the alternative direction is walkable, diggable, ...)
12023 =============================================================================
12026 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12027 int x, int y, int real_dx, int real_dy)
12029 int jx, jy, dx, dy, xx, yy;
12031 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12034 /* diagonal direction: check alternative direction */
12039 xx = jx + (dx == 0 ? real_dx : 0);
12040 yy = jy + (dy == 0 ? real_dy : 0);
12042 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12046 =============================================================================
12048 -----------------------------------------------------------------------------
12049 x, y: field next to player (non-diagonal) to try to dig to
12050 real_dx, real_dy: direction as read from input device (can be diagonal)
12051 =============================================================================
12054 int DigField(struct PlayerInfo *player,
12055 int oldx, int oldy, int x, int y,
12056 int real_dx, int real_dy, int mode)
12058 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12059 boolean player_was_pushing = player->is_pushing;
12060 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12061 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12062 int jx = oldx, jy = oldy;
12063 int dx = x - jx, dy = y - jy;
12064 int nextx = x + dx, nexty = y + dy;
12065 int move_direction = (dx == -1 ? MV_LEFT :
12066 dx == +1 ? MV_RIGHT :
12068 dy == +1 ? MV_DOWN : MV_NONE);
12069 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12070 int dig_side = MV_DIR_OPPOSITE(move_direction);
12071 int old_element = Feld[jx][jy];
12072 #if USE_FIXED_DONT_RUN_INTO
12073 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12079 if (is_player) /* function can also be called by EL_PENGUIN */
12081 if (player->MovPos == 0)
12083 player->is_digging = FALSE;
12084 player->is_collecting = FALSE;
12087 if (player->MovPos == 0) /* last pushing move finished */
12088 player->is_pushing = FALSE;
12090 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12092 player->is_switching = FALSE;
12093 player->push_delay = -1;
12095 return MP_NO_ACTION;
12099 #if !USE_FIXED_DONT_RUN_INTO
12100 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12101 return MP_NO_ACTION;
12104 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12105 old_element = Back[jx][jy];
12107 /* in case of element dropped at player position, check background */
12108 else if (Back[jx][jy] != EL_EMPTY &&
12109 game.engine_version >= VERSION_IDENT(2,2,0,0))
12110 old_element = Back[jx][jy];
12112 /* checking here causes player to move into acid even if the current field
12113 cannot be left to that direction */
12115 #if USE_FIXED_DONT_RUN_INTO
12116 if (player_can_move && DONT_RUN_INTO(element))
12118 if (element == EL_ACID && dx == 0 && dy == 1)
12121 Feld[jx][jy] = EL_PLAYER_1;
12122 InitMovingField(jx, jy, MV_DOWN);
12123 Store[jx][jy] = EL_ACID;
12124 ContinueMoving(jx, jy);
12125 BuryPlayer(player);
12128 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12130 return MP_DONT_RUN_INTO;
12135 #if 1 /* ------------------------------ NEW ------------------------------ */
12137 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12138 return MP_NO_ACTION; /* field has no opening in this direction */
12140 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12141 return MP_NO_ACTION; /* field has no opening in this direction */
12143 #if USE_FIXED_DONT_RUN_INTO
12144 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12148 Feld[jx][jy] = player->artwork_element;
12150 Feld[jx][jy] = EL_PLAYER_1;
12152 InitMovingField(jx, jy, MV_DOWN);
12153 Store[jx][jy] = EL_ACID;
12154 ContinueMoving(jx, jy);
12155 BuryPlayer(player);
12157 return MP_DONT_RUN_INTO;
12161 #if USE_FIXED_DONT_RUN_INTO
12162 if (player_can_move && DONT_RUN_INTO(element))
12164 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12166 return MP_DONT_RUN_INTO;
12170 #else /* ------------------------------ OLD ------------------------------ */
12173 #if USE_FIXED_DONT_RUN_INTO
12174 if (player_can_move && DONT_RUN_INTO(element))
12176 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12178 return MP_DONT_RUN_INTO;
12183 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12184 return MP_NO_ACTION; /* field has no opening in this direction */
12186 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12187 return MP_NO_ACTION; /* field has no opening in this direction */
12189 /* checking here causes player to explode when moving into acid */
12191 #if USE_FIXED_DONT_RUN_INTO
12192 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12195 Feld[jx][jy] = EL_PLAYER_1;
12196 InitMovingField(jx, jy, MV_DOWN);
12197 Store[jx][jy] = EL_ACID;
12198 ContinueMoving(jx, jy);
12199 BuryPlayer(player);
12201 return MP_DONT_RUN_INTO;
12206 #endif /* ------------------------------ END ------------------------------ */
12209 #if USE_FIXED_DONT_RUN_INTO
12210 if (player_can_move && DONT_RUN_INTO(element))
12212 if (element == EL_ACID && dx == 0 && dy == 1)
12215 Feld[jx][jy] = EL_PLAYER_1;
12216 InitMovingField(jx, jy, MV_DOWN);
12217 Store[jx][jy] = EL_ACID;
12218 ContinueMoving(jx, jy);
12219 BuryPlayer(player);
12222 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12224 return MP_DONT_RUN_INTO;
12229 #if USE_FIXED_DONT_RUN_INTO
12230 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12231 return MP_NO_ACTION;
12234 #if !USE_FIXED_DONT_RUN_INTO
12235 element = Feld[x][y];
12238 collect_count = element_info[element].collect_count_initial;
12240 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12241 return MP_NO_ACTION;
12243 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12244 player_can_move = player_can_move_or_snap;
12246 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12247 game.engine_version >= VERSION_IDENT(2,2,0,0))
12249 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12250 player->index_bit, dig_side);
12251 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12252 player->index_bit, dig_side);
12254 if (Feld[x][y] != element) /* field changed by snapping */
12257 return MP_NO_ACTION;
12260 #if USE_PLAYER_GRAVITY
12261 if (player->gravity && is_player && !player->is_auto_moving &&
12262 canFallDown(player) && move_direction != MV_DOWN &&
12263 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12264 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12266 if (game.gravity && is_player && !player->is_auto_moving &&
12267 canFallDown(player) && move_direction != MV_DOWN &&
12268 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12269 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12272 if (player_can_move &&
12273 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12275 int sound_element = SND_ELEMENT(element);
12276 int sound_action = ACTION_WALKING;
12278 if (IS_RND_GATE(element))
12280 if (!player->key[RND_GATE_NR(element)])
12281 return MP_NO_ACTION;
12283 else if (IS_RND_GATE_GRAY(element))
12285 if (!player->key[RND_GATE_GRAY_NR(element)])
12286 return MP_NO_ACTION;
12288 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12290 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12291 return MP_NO_ACTION;
12293 else if (element == EL_EXIT_OPEN ||
12294 element == EL_SP_EXIT_OPEN ||
12295 element == EL_SP_EXIT_OPENING)
12297 sound_action = ACTION_PASSING; /* player is passing exit */
12299 else if (element == EL_EMPTY)
12301 sound_action = ACTION_MOVING; /* nothing to walk on */
12304 /* play sound from background or player, whatever is available */
12305 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12306 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12308 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12310 else if (player_can_move &&
12311 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12313 if (!ACCESS_FROM(element, opposite_direction))
12314 return MP_NO_ACTION; /* field not accessible from this direction */
12316 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12317 return MP_NO_ACTION;
12319 if (IS_EM_GATE(element))
12321 if (!player->key[EM_GATE_NR(element)])
12322 return MP_NO_ACTION;
12324 else if (IS_EM_GATE_GRAY(element))
12326 if (!player->key[EM_GATE_GRAY_NR(element)])
12327 return MP_NO_ACTION;
12329 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12331 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12332 return MP_NO_ACTION;
12334 else if (IS_EMC_GATE(element))
12336 if (!player->key[EMC_GATE_NR(element)])
12337 return MP_NO_ACTION;
12339 else if (IS_EMC_GATE_GRAY(element))
12341 if (!player->key[EMC_GATE_GRAY_NR(element)])
12342 return MP_NO_ACTION;
12344 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12346 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12347 return MP_NO_ACTION;
12349 else if (IS_SP_PORT(element))
12351 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12352 element == EL_SP_GRAVITY_PORT_RIGHT ||
12353 element == EL_SP_GRAVITY_PORT_UP ||
12354 element == EL_SP_GRAVITY_PORT_DOWN)
12355 #if USE_PLAYER_GRAVITY
12356 player->gravity = !player->gravity;
12358 game.gravity = !game.gravity;
12360 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12361 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12362 element == EL_SP_GRAVITY_ON_PORT_UP ||
12363 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12364 #if USE_PLAYER_GRAVITY
12365 player->gravity = TRUE;
12367 game.gravity = TRUE;
12369 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12370 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12371 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12372 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12373 #if USE_PLAYER_GRAVITY
12374 player->gravity = FALSE;
12376 game.gravity = FALSE;
12380 /* automatically move to the next field with double speed */
12381 player->programmed_action = move_direction;
12383 if (player->move_delay_reset_counter == 0)
12385 player->move_delay_reset_counter = 2; /* two double speed steps */
12387 DOUBLE_PLAYER_SPEED(player);
12390 PlayLevelSoundAction(x, y, ACTION_PASSING);
12392 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12396 if (mode != DF_SNAP)
12398 GfxElement[x][y] = GFX_ELEMENT(element);
12399 player->is_digging = TRUE;
12402 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12404 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12405 player->index_bit, dig_side);
12407 if (mode == DF_SNAP)
12409 #if USE_NEW_SNAP_DELAY
12410 if (level.block_snap_field)
12411 setFieldForSnapping(x, y, element, move_direction);
12413 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12415 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12418 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12419 player->index_bit, dig_side);
12422 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12426 if (is_player && mode != DF_SNAP)
12428 GfxElement[x][y] = element;
12429 player->is_collecting = TRUE;
12432 if (element == EL_SPEED_PILL)
12434 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12436 else if (element == EL_EXTRA_TIME && level.time > 0)
12438 TimeLeft += level.extra_time;
12439 DrawGameValue_Time(TimeLeft);
12441 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12443 player->shield_normal_time_left += level.shield_normal_time;
12444 if (element == EL_SHIELD_DEADLY)
12445 player->shield_deadly_time_left += level.shield_deadly_time;
12447 else if (element == EL_DYNAMITE ||
12448 element == EL_EM_DYNAMITE ||
12449 element == EL_SP_DISK_RED)
12451 if (player->inventory_size < MAX_INVENTORY_SIZE)
12452 player->inventory_element[player->inventory_size++] = element;
12455 DrawGameDoorValues();
12457 DrawGameValue_Dynamite(local_player->inventory_size);
12460 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12462 player->dynabomb_count++;
12463 player->dynabombs_left++;
12465 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12467 player->dynabomb_size++;
12469 else if (element == EL_DYNABOMB_INCREASE_POWER)
12471 player->dynabomb_xl = TRUE;
12473 else if (IS_KEY(element))
12475 player->key[KEY_NR(element)] = TRUE;
12478 DrawGameDoorValues();
12480 DrawGameValue_Keys(player->key);
12483 redraw_mask |= REDRAW_DOOR_1;
12485 else if (IS_ENVELOPE(element))
12487 player->show_envelope = element;
12489 else if (element == EL_EMC_LENSES)
12491 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12493 RedrawAllInvisibleElementsForLenses();
12495 else if (element == EL_EMC_MAGNIFIER)
12497 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12499 RedrawAllInvisibleElementsForMagnifier();
12501 else if (IS_DROPPABLE(element) ||
12502 IS_THROWABLE(element)) /* can be collected and dropped */
12506 if (collect_count == 0)
12507 player->inventory_infinite_element = element;
12509 for (i = 0; i < collect_count; i++)
12510 if (player->inventory_size < MAX_INVENTORY_SIZE)
12511 player->inventory_element[player->inventory_size++] = element;
12514 DrawGameDoorValues();
12516 DrawGameValue_Dynamite(local_player->inventory_size);
12519 else if (collect_count > 0)
12521 local_player->gems_still_needed -= collect_count;
12522 if (local_player->gems_still_needed < 0)
12523 local_player->gems_still_needed = 0;
12525 DrawGameValue_Emeralds(local_player->gems_still_needed);
12528 RaiseScoreElement(element);
12529 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12532 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12533 player->index_bit, dig_side);
12535 if (mode == DF_SNAP)
12537 #if USE_NEW_SNAP_DELAY
12538 if (level.block_snap_field)
12539 setFieldForSnapping(x, y, element, move_direction);
12541 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12543 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12546 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12547 player->index_bit, dig_side);
12550 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12552 if (mode == DF_SNAP && element != EL_BD_ROCK)
12553 return MP_NO_ACTION;
12555 if (CAN_FALL(element) && dy)
12556 return MP_NO_ACTION;
12558 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12559 !(element == EL_SPRING && level.use_spring_bug))
12560 return MP_NO_ACTION;
12562 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12563 ((move_direction & MV_VERTICAL &&
12564 ((element_info[element].move_pattern & MV_LEFT &&
12565 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12566 (element_info[element].move_pattern & MV_RIGHT &&
12567 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12568 (move_direction & MV_HORIZONTAL &&
12569 ((element_info[element].move_pattern & MV_UP &&
12570 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12571 (element_info[element].move_pattern & MV_DOWN &&
12572 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12573 return MP_NO_ACTION;
12575 /* do not push elements already moving away faster than player */
12576 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12577 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12578 return MP_NO_ACTION;
12580 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12582 if (player->push_delay_value == -1 || !player_was_pushing)
12583 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12585 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12587 if (player->push_delay_value == -1)
12588 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12590 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12592 if (!player->is_pushing)
12593 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12596 player->is_pushing = TRUE;
12597 player->is_active = TRUE;
12599 if (!(IN_LEV_FIELD(nextx, nexty) &&
12600 (IS_FREE(nextx, nexty) ||
12601 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12602 IS_SB_ELEMENT(element)))))
12603 return MP_NO_ACTION;
12605 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12606 return MP_NO_ACTION;
12608 if (player->push_delay == -1) /* new pushing; restart delay */
12609 player->push_delay = 0;
12611 if (player->push_delay < player->push_delay_value &&
12612 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12613 element != EL_SPRING && element != EL_BALLOON)
12615 /* make sure that there is no move delay before next try to push */
12616 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12617 player->move_delay = 0;
12619 return MP_NO_ACTION;
12622 if (IS_SB_ELEMENT(element))
12624 if (element == EL_SOKOBAN_FIELD_FULL)
12626 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12627 local_player->sokobanfields_still_needed++;
12630 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12632 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12633 local_player->sokobanfields_still_needed--;
12636 Feld[x][y] = EL_SOKOBAN_OBJECT;
12638 if (Back[x][y] == Back[nextx][nexty])
12639 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12640 else if (Back[x][y] != 0)
12641 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12644 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12647 if (local_player->sokobanfields_still_needed == 0 &&
12648 game.emulation == EMU_SOKOBAN)
12650 player->LevelSolved = player->GameOver = TRUE;
12651 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12655 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12657 InitMovingField(x, y, move_direction);
12658 GfxAction[x][y] = ACTION_PUSHING;
12660 if (mode == DF_SNAP)
12661 ContinueMoving(x, y);
12663 MovPos[x][y] = (dx != 0 ? dx : dy);
12665 Pushed[x][y] = TRUE;
12666 Pushed[nextx][nexty] = TRUE;
12668 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12669 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12671 player->push_delay_value = -1; /* get new value later */
12673 /* check for element change _after_ element has been pushed */
12674 if (game.use_change_when_pushing_bug)
12676 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12677 player->index_bit, dig_side);
12678 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12679 player->index_bit, dig_side);
12682 else if (IS_SWITCHABLE(element))
12684 if (PLAYER_SWITCHING(player, x, y))
12686 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12687 player->index_bit, dig_side);
12692 player->is_switching = TRUE;
12693 player->switch_x = x;
12694 player->switch_y = y;
12696 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12698 if (element == EL_ROBOT_WHEEL)
12700 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12704 DrawLevelField(x, y);
12706 else if (element == EL_SP_TERMINAL)
12711 SCAN_PLAYFIELD(xx, yy)
12713 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12716 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12718 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12719 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12722 else if (IS_BELT_SWITCH(element))
12724 ToggleBeltSwitch(x, y);
12726 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12727 element == EL_SWITCHGATE_SWITCH_DOWN)
12729 ToggleSwitchgateSwitch(x, y);
12731 else if (element == EL_LIGHT_SWITCH ||
12732 element == EL_LIGHT_SWITCH_ACTIVE)
12734 ToggleLightSwitch(x, y);
12736 else if (element == EL_TIMEGATE_SWITCH)
12738 ActivateTimegateSwitch(x, y);
12740 else if (element == EL_BALLOON_SWITCH_LEFT ||
12741 element == EL_BALLOON_SWITCH_RIGHT ||
12742 element == EL_BALLOON_SWITCH_UP ||
12743 element == EL_BALLOON_SWITCH_DOWN ||
12744 element == EL_BALLOON_SWITCH_NONE ||
12745 element == EL_BALLOON_SWITCH_ANY)
12747 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12748 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12749 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12750 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12751 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12754 else if (element == EL_LAMP)
12756 Feld[x][y] = EL_LAMP_ACTIVE;
12757 local_player->lights_still_needed--;
12759 ResetGfxAnimation(x, y);
12760 DrawLevelField(x, y);
12762 else if (element == EL_TIME_ORB_FULL)
12764 Feld[x][y] = EL_TIME_ORB_EMPTY;
12766 if (level.time > 0 || level.use_time_orb_bug)
12768 TimeLeft += level.time_orb_time;
12769 DrawGameValue_Time(TimeLeft);
12772 ResetGfxAnimation(x, y);
12773 DrawLevelField(x, y);
12775 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12776 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12780 game.ball_state = !game.ball_state;
12783 SCAN_PLAYFIELD(xx, yy)
12785 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12788 int e = Feld[xx][yy];
12790 if (game.ball_state)
12792 if (e == EL_EMC_MAGIC_BALL)
12793 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12794 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12795 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12799 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12800 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12801 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12802 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12807 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12808 player->index_bit, dig_side);
12810 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12811 player->index_bit, dig_side);
12813 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12814 player->index_bit, dig_side);
12820 if (!PLAYER_SWITCHING(player, x, y))
12822 player->is_switching = TRUE;
12823 player->switch_x = x;
12824 player->switch_y = y;
12826 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12827 player->index_bit, dig_side);
12828 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12829 player->index_bit, dig_side);
12831 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12832 player->index_bit, dig_side);
12833 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12834 player->index_bit, dig_side);
12837 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12838 player->index_bit, dig_side);
12839 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12840 player->index_bit, dig_side);
12842 return MP_NO_ACTION;
12845 player->push_delay = -1;
12847 if (is_player) /* function can also be called by EL_PENGUIN */
12849 if (Feld[x][y] != element) /* really digged/collected something */
12851 player->is_collecting = !player->is_digging;
12852 player->is_active = TRUE;
12859 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12861 int jx = player->jx, jy = player->jy;
12862 int x = jx + dx, y = jy + dy;
12863 int snap_direction = (dx == -1 ? MV_LEFT :
12864 dx == +1 ? MV_RIGHT :
12866 dy == +1 ? MV_DOWN : MV_NONE);
12867 boolean can_continue_snapping = (level.continuous_snapping &&
12868 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12870 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12873 if (!player->active || !IN_LEV_FIELD(x, y))
12881 if (player->MovPos == 0)
12882 player->is_pushing = FALSE;
12884 player->is_snapping = FALSE;
12886 if (player->MovPos == 0)
12888 player->is_moving = FALSE;
12889 player->is_digging = FALSE;
12890 player->is_collecting = FALSE;
12896 #if USE_NEW_CONTINUOUS_SNAPPING
12897 /* prevent snapping with already pressed snap key when not allowed */
12898 if (player->is_snapping && !can_continue_snapping)
12901 if (player->is_snapping)
12905 player->MovDir = snap_direction;
12907 if (player->MovPos == 0)
12909 player->is_moving = FALSE;
12910 player->is_digging = FALSE;
12911 player->is_collecting = FALSE;
12914 player->is_dropping = FALSE;
12915 player->is_dropping_pressed = FALSE;
12916 player->drop_pressed_delay = 0;
12918 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12921 player->is_snapping = TRUE;
12922 player->is_active = TRUE;
12924 if (player->MovPos == 0)
12926 player->is_moving = FALSE;
12927 player->is_digging = FALSE;
12928 player->is_collecting = FALSE;
12931 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12932 DrawLevelField(player->last_jx, player->last_jy);
12934 DrawLevelField(x, y);
12939 boolean DropElement(struct PlayerInfo *player)
12941 int old_element, new_element;
12942 int dropx = player->jx, dropy = player->jy;
12943 int drop_direction = player->MovDir;
12944 int drop_side = drop_direction;
12945 int drop_element = (player->inventory_size > 0 ?
12946 player->inventory_element[player->inventory_size - 1] :
12947 player->inventory_infinite_element != EL_UNDEFINED ?
12948 player->inventory_infinite_element :
12949 player->dynabombs_left > 0 ?
12950 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12953 player->is_dropping_pressed = TRUE;
12955 /* do not drop an element on top of another element; when holding drop key
12956 pressed without moving, dropped element must move away before the next
12957 element can be dropped (this is especially important if the next element
12958 is dynamite, which can be placed on background for historical reasons) */
12959 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12962 if (IS_THROWABLE(drop_element))
12964 dropx += GET_DX_FROM_DIR(drop_direction);
12965 dropy += GET_DY_FROM_DIR(drop_direction);
12967 if (!IN_LEV_FIELD(dropx, dropy))
12971 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12972 new_element = drop_element; /* default: no change when dropping */
12974 /* check if player is active, not moving and ready to drop */
12975 if (!player->active || player->MovPos || player->drop_delay > 0)
12978 /* check if player has anything that can be dropped */
12979 if (new_element == EL_UNDEFINED)
12982 /* check if drop key was pressed long enough for EM style dynamite */
12983 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12986 /* check if anything can be dropped at the current position */
12987 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12990 /* collected custom elements can only be dropped on empty fields */
12991 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12994 if (old_element != EL_EMPTY)
12995 Back[dropx][dropy] = old_element; /* store old element on this field */
12997 ResetGfxAnimation(dropx, dropy);
12998 ResetRandomAnimationValue(dropx, dropy);
13000 if (player->inventory_size > 0 ||
13001 player->inventory_infinite_element != EL_UNDEFINED)
13003 if (player->inventory_size > 0)
13005 player->inventory_size--;
13008 DrawGameDoorValues();
13010 DrawGameValue_Dynamite(local_player->inventory_size);
13013 if (new_element == EL_DYNAMITE)
13014 new_element = EL_DYNAMITE_ACTIVE;
13015 else if (new_element == EL_EM_DYNAMITE)
13016 new_element = EL_EM_DYNAMITE_ACTIVE;
13017 else if (new_element == EL_SP_DISK_RED)
13018 new_element = EL_SP_DISK_RED_ACTIVE;
13021 Feld[dropx][dropy] = new_element;
13023 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13024 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13025 el2img(Feld[dropx][dropy]), 0);
13027 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13029 /* needed if previous element just changed to "empty" in the last frame */
13030 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13032 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13033 player->index_bit, drop_side);
13034 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13036 player->index_bit, drop_side);
13038 TestIfElementTouchesCustomElement(dropx, dropy);
13040 else /* player is dropping a dyna bomb */
13042 player->dynabombs_left--;
13044 Feld[dropx][dropy] = new_element;
13046 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13047 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13048 el2img(Feld[dropx][dropy]), 0);
13050 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13053 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13054 InitField_WithBug1(dropx, dropy, FALSE);
13056 new_element = Feld[dropx][dropy]; /* element might have changed */
13058 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13059 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13061 int move_direction, nextx, nexty;
13063 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13064 MovDir[dropx][dropy] = drop_direction;
13066 move_direction = MovDir[dropx][dropy];
13067 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13068 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13070 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13071 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13074 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13075 player->is_dropping = TRUE;
13077 player->drop_pressed_delay = 0;
13078 player->is_dropping_pressed = FALSE;
13080 player->drop_x = dropx;
13081 player->drop_y = dropy;
13086 /* ------------------------------------------------------------------------- */
13087 /* game sound playing functions */
13088 /* ------------------------------------------------------------------------- */
13090 static int *loop_sound_frame = NULL;
13091 static int *loop_sound_volume = NULL;
13093 void InitPlayLevelSound()
13095 int num_sounds = getSoundListSize();
13097 checked_free(loop_sound_frame);
13098 checked_free(loop_sound_volume);
13100 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13101 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13104 static void PlayLevelSound(int x, int y, int nr)
13106 int sx = SCREENX(x), sy = SCREENY(y);
13107 int volume, stereo_position;
13108 int max_distance = 8;
13109 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13111 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13112 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13115 if (!IN_LEV_FIELD(x, y) ||
13116 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13117 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13120 volume = SOUND_MAX_VOLUME;
13122 if (!IN_SCR_FIELD(sx, sy))
13124 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13125 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13127 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13130 stereo_position = (SOUND_MAX_LEFT +
13131 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13132 (SCR_FIELDX + 2 * max_distance));
13134 if (IS_LOOP_SOUND(nr))
13136 /* This assures that quieter loop sounds do not overwrite louder ones,
13137 while restarting sound volume comparison with each new game frame. */
13139 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13142 loop_sound_volume[nr] = volume;
13143 loop_sound_frame[nr] = FrameCounter;
13146 PlaySoundExt(nr, volume, stereo_position, type);
13149 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13151 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13152 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13153 y < LEVELY(BY1) ? LEVELY(BY1) :
13154 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13158 static void PlayLevelSoundAction(int x, int y, int action)
13160 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13163 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13165 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13167 if (sound_effect != SND_UNDEFINED)
13168 PlayLevelSound(x, y, sound_effect);
13171 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13174 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13176 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13177 PlayLevelSound(x, y, sound_effect);
13180 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13182 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13184 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13185 PlayLevelSound(x, y, sound_effect);
13188 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13190 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13192 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13193 StopSound(sound_effect);
13196 static void PlayLevelMusic()
13198 if (levelset.music[level_nr] != MUS_UNDEFINED)
13199 PlayMusic(levelset.music[level_nr]); /* from config file */
13201 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13204 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
13206 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13211 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13215 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13219 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13223 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13227 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13231 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13235 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13238 case SAMPLE_android_clone:
13239 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13242 case SAMPLE_android_move:
13243 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13246 case SAMPLE_spring:
13247 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13251 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13255 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13258 case SAMPLE_eater_eat:
13259 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13263 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13266 case SAMPLE_collect:
13267 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13270 case SAMPLE_diamond:
13271 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13274 case SAMPLE_squash:
13275 /* !!! CHECK THIS !!! */
13277 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13279 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13283 case SAMPLE_wonderfall:
13284 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13288 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13292 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13296 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13300 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13304 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13308 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13311 case SAMPLE_wonder:
13312 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13316 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13319 case SAMPLE_exit_open:
13320 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13323 case SAMPLE_exit_leave:
13324 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13327 case SAMPLE_dynamite:
13328 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13332 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13336 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13340 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13344 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13348 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13352 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13356 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13361 void RaiseScore(int value)
13363 local_player->score += value;
13365 DrawGameValue_Score(local_player->score);
13368 void RaiseScoreElement(int element)
13373 case EL_BD_DIAMOND:
13374 case EL_EMERALD_YELLOW:
13375 case EL_EMERALD_RED:
13376 case EL_EMERALD_PURPLE:
13377 case EL_SP_INFOTRON:
13378 RaiseScore(level.score[SC_EMERALD]);
13381 RaiseScore(level.score[SC_DIAMOND]);
13384 RaiseScore(level.score[SC_CRYSTAL]);
13387 RaiseScore(level.score[SC_PEARL]);
13390 case EL_BD_BUTTERFLY:
13391 case EL_SP_ELECTRON:
13392 RaiseScore(level.score[SC_BUG]);
13395 case EL_BD_FIREFLY:
13396 case EL_SP_SNIKSNAK:
13397 RaiseScore(level.score[SC_SPACESHIP]);
13400 case EL_DARK_YAMYAM:
13401 RaiseScore(level.score[SC_YAMYAM]);
13404 RaiseScore(level.score[SC_ROBOT]);
13407 RaiseScore(level.score[SC_PACMAN]);
13410 RaiseScore(level.score[SC_NUT]);
13413 case EL_EM_DYNAMITE:
13414 case EL_SP_DISK_RED:
13415 case EL_DYNABOMB_INCREASE_NUMBER:
13416 case EL_DYNABOMB_INCREASE_SIZE:
13417 case EL_DYNABOMB_INCREASE_POWER:
13418 RaiseScore(level.score[SC_DYNAMITE]);
13420 case EL_SHIELD_NORMAL:
13421 case EL_SHIELD_DEADLY:
13422 RaiseScore(level.score[SC_SHIELD]);
13424 case EL_EXTRA_TIME:
13425 RaiseScore(level.extra_time_score);
13439 RaiseScore(level.score[SC_KEY]);
13442 RaiseScore(element_info[element].collect_score);
13447 void RequestQuitGame(boolean ask_if_really_quit)
13449 if (AllPlayersGone ||
13450 !ask_if_really_quit ||
13451 level_editor_test_game ||
13452 Request("Do you really want to quit the game ?",
13453 REQ_ASK | REQ_STAY_CLOSED))
13455 #if defined(NETWORK_AVALIABLE)
13456 if (options.network)
13457 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13461 if (!ask_if_really_quit || level_editor_test_game)
13463 game_status = GAME_MODE_MAIN;
13469 FadeOut(REDRAW_FIELD);
13471 game_status = GAME_MODE_MAIN;
13473 DrawAndFadeInMainMenu(REDRAW_FIELD);
13479 if (tape.playing && tape.deactivate_display)
13480 TapeDeactivateDisplayOff(TRUE);
13482 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13484 if (tape.playing && tape.deactivate_display)
13485 TapeDeactivateDisplayOn();
13490 /* ---------- new game button stuff ---------------------------------------- */
13492 /* graphic position values for game buttons */
13493 #define GAME_BUTTON_XSIZE 30
13494 #define GAME_BUTTON_YSIZE 30
13495 #define GAME_BUTTON_XPOS 5
13496 #define GAME_BUTTON_YPOS 215
13497 #define SOUND_BUTTON_XPOS 5
13498 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13500 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13501 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13502 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13503 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13504 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13505 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13512 } gamebutton_info[NUM_GAME_BUTTONS] =
13515 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13520 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13521 GAME_CTRL_ID_PAUSE,
13525 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13530 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13531 SOUND_CTRL_ID_MUSIC,
13532 "background music on/off"
13535 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13536 SOUND_CTRL_ID_LOOPS,
13537 "sound loops on/off"
13540 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13541 SOUND_CTRL_ID_SIMPLE,
13542 "normal sounds on/off"
13546 void CreateGameButtons()
13550 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13552 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13553 struct GadgetInfo *gi;
13556 unsigned long event_mask;
13557 int gd_xoffset, gd_yoffset;
13558 int gd_x1, gd_x2, gd_y1, gd_y2;
13561 gd_xoffset = gamebutton_info[i].x;
13562 gd_yoffset = gamebutton_info[i].y;
13563 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13564 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13566 if (id == GAME_CTRL_ID_STOP ||
13567 id == GAME_CTRL_ID_PAUSE ||
13568 id == GAME_CTRL_ID_PLAY)
13570 button_type = GD_TYPE_NORMAL_BUTTON;
13572 event_mask = GD_EVENT_RELEASED;
13573 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13574 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13578 button_type = GD_TYPE_CHECK_BUTTON;
13580 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13581 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13582 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13583 event_mask = GD_EVENT_PRESSED;
13584 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13585 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13588 gi = CreateGadget(GDI_CUSTOM_ID, id,
13589 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13590 GDI_X, DX + gd_xoffset,
13591 GDI_Y, DY + gd_yoffset,
13592 GDI_WIDTH, GAME_BUTTON_XSIZE,
13593 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13594 GDI_TYPE, button_type,
13595 GDI_STATE, GD_BUTTON_UNPRESSED,
13596 GDI_CHECKED, checked,
13597 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13598 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13599 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13600 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13601 GDI_EVENT_MASK, event_mask,
13602 GDI_CALLBACK_ACTION, HandleGameButtons,
13606 Error(ERR_EXIT, "cannot create gadget");
13608 game_gadget[id] = gi;
13612 void FreeGameButtons()
13616 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13617 FreeGadget(game_gadget[i]);
13620 static void MapGameButtons()
13624 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13625 MapGadget(game_gadget[i]);
13628 void UnmapGameButtons()
13632 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13633 UnmapGadget(game_gadget[i]);
13636 static void HandleGameButtons(struct GadgetInfo *gi)
13638 int id = gi->custom_id;
13640 if (game_status != GAME_MODE_PLAYING)
13645 case GAME_CTRL_ID_STOP:
13649 RequestQuitGame(TRUE);
13652 case GAME_CTRL_ID_PAUSE:
13653 if (options.network)
13655 #if defined(NETWORK_AVALIABLE)
13657 SendToServer_ContinuePlaying();
13659 SendToServer_PausePlaying();
13663 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13666 case GAME_CTRL_ID_PLAY:
13669 #if defined(NETWORK_AVALIABLE)
13670 if (options.network)
13671 SendToServer_ContinuePlaying();
13675 tape.pausing = FALSE;
13676 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13681 case SOUND_CTRL_ID_MUSIC:
13682 if (setup.sound_music)
13684 setup.sound_music = FALSE;
13687 else if (audio.music_available)
13689 setup.sound = setup.sound_music = TRUE;
13691 SetAudioMode(setup.sound);
13697 case SOUND_CTRL_ID_LOOPS:
13698 if (setup.sound_loops)
13699 setup.sound_loops = FALSE;
13700 else if (audio.loops_available)
13702 setup.sound = setup.sound_loops = TRUE;
13703 SetAudioMode(setup.sound);
13707 case SOUND_CTRL_ID_SIMPLE:
13708 if (setup.sound_simple)
13709 setup.sound_simple = FALSE;
13710 else if (audio.sound_available)
13712 setup.sound = setup.sound_simple = TRUE;
13713 SetAudioMode(setup.sound);