1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
68 /* for MovePlayer() */
69 #define MP_NO_ACTION 0
72 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
74 /* for ScrollPlayer() */
76 #define SCROLL_GO_ON 1
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START 0
80 #define EX_TYPE_NONE 0
81 #define EX_TYPE_NORMAL (1 << 0)
82 #define EX_TYPE_CENTER (1 << 1)
83 #define EX_TYPE_BORDER (1 << 2)
84 #define EX_TYPE_CROSS (1 << 3)
85 #define EX_TYPE_DYNA (1 << 4)
86 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
88 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
90 /* special positions in the game control window (relative to control window) */
91 #define XX_LEVEL1 (game.panel.level.x)
92 #define XX_LEVEL2 (game.panel.level.x - 1)
93 #define YY_LEVEL (game.panel.level.y)
94 #define XX_EMERALDS (game.panel.gems.x)
95 #define YY_EMERALDS (game.panel.gems.y)
96 #define XX_DYNAMITE (game.panel.inventory.x)
97 #define YY_DYNAMITE (game.panel.inventory.y)
98 #define XX_KEYS (game.panel.keys.x)
99 #define YY_KEYS (game.panel.keys.y)
100 #define XX_SCORE (game.panel.score.x)
101 #define YY_SCORE (game.panel.score.y)
102 #define XX_TIME1 (game.panel.time.x)
103 #define XX_TIME2 (game.panel.time.x + 1)
104 #define YY_TIME (game.panel.time.y)
106 /* special positions in the game control window (relative to main window) */
107 #define DX_LEVEL1 (DX + XX_LEVEL1)
108 #define DX_LEVEL2 (DX + XX_LEVEL2)
109 #define DY_LEVEL (DY + YY_LEVEL)
110 #define DX_EMERALDS (DX + XX_EMERALDS)
111 #define DY_EMERALDS (DY + YY_EMERALDS)
112 #define DX_DYNAMITE (DX + XX_DYNAMITE)
113 #define DY_DYNAMITE (DY + YY_DYNAMITE)
114 #define DX_KEYS (DX + XX_KEYS)
115 #define DY_KEYS (DY + YY_KEYS)
116 #define DX_SCORE (DX + XX_SCORE)
117 #define DY_SCORE (DY + YY_SCORE)
118 #define DX_TIME1 (DX + XX_TIME1)
119 #define DX_TIME2 (DX + XX_TIME2)
120 #define DY_TIME (DY + YY_TIME)
122 /* values for delayed check of falling and moving elements and for collision */
123 #define CHECK_DELAY_MOVING 3
124 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
125 #define CHECK_DELAY_COLLISION 2
126 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
128 /* values for initial player move delay (initial delay counter value) */
129 #define INITIAL_MOVE_DELAY_OFF -1
130 #define INITIAL_MOVE_DELAY_ON 0
132 /* values for player movement speed (which is in fact a delay value) */
133 #define MOVE_DELAY_MIN_SPEED 32
134 #define MOVE_DELAY_NORMAL_SPEED 8
135 #define MOVE_DELAY_HIGH_SPEED 4
136 #define MOVE_DELAY_MAX_SPEED 1
138 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
139 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
141 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
142 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
144 /* values for other actions */
145 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
146 #define MOVE_STEPSIZE_MIN (1)
147 #define MOVE_STEPSIZE_MAX (TILEX)
149 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
150 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
152 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
154 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
155 RND(element_info[e].push_delay_random))
156 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
157 RND(element_info[e].drop_delay_random))
158 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
159 RND(element_info[e].move_delay_random))
160 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
161 (element_info[e].move_delay_random))
162 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
163 RND(element_info[e].ce_value_random_initial))
164 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
165 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
166 RND((c)->delay_random * (c)->delay_frames))
167 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
168 RND((c)->delay_random))
171 #define GET_VALID_RUNTIME_ELEMENT(e) \
172 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
174 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
175 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
176 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
177 (be) + (e) - EL_SELF)
179 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
180 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
181 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
182 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
183 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
184 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
185 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
186 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
187 RESOLVED_REFERENCE_ELEMENT(be, e) : \
190 #define CAN_GROW_INTO(e) \
191 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
193 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
194 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
197 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
199 (CAN_MOVE_INTO_ACID(e) && \
200 Feld[x][y] == EL_ACID) || \
203 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
212 (CAN_MOVE_INTO_ACID(e) && \
213 Feld[x][y] == EL_ACID) || \
214 (DONT_COLLIDE_WITH(e) && \
216 !PLAYER_ENEMY_PROTECTED(x, y))))
218 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
221 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
224 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
227 #define ANDROID_CAN_CLONE_FIELD(x, y) \
228 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
229 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
231 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
234 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
237 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
240 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
243 #define PIG_CAN_ENTER_FIELD(e, x, y) \
244 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
246 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
248 IS_FOOD_PENGUIN(Feld[x][y])))
249 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
250 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
252 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
255 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
258 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
259 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
260 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
262 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
264 #define CE_ENTER_FIELD_COND(e, x, y) \
265 (!IS_PLAYER(x, y) && \
266 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
268 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
269 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
271 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
272 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
274 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
275 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
276 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
277 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
279 /* game button identifiers */
280 #define GAME_CTRL_ID_STOP 0
281 #define GAME_CTRL_ID_PAUSE 1
282 #define GAME_CTRL_ID_PLAY 2
283 #define SOUND_CTRL_ID_MUSIC 3
284 #define SOUND_CTRL_ID_LOOPS 4
285 #define SOUND_CTRL_ID_SIMPLE 5
287 #define NUM_GAME_BUTTONS 6
290 /* forward declaration for internal use */
292 static void CreateField(int, int, int);
294 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
295 static void AdvanceFrameAndPlayerCounters(int);
297 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
298 static boolean MovePlayer(struct PlayerInfo *, int, int);
299 static void ScrollPlayer(struct PlayerInfo *, int);
300 static void ScrollScreen(struct PlayerInfo *, int);
302 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
304 static void InitBeltMovement(void);
305 static void CloseAllOpenTimegates(void);
306 static void CheckGravityMovement(struct PlayerInfo *);
307 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
308 static void KillPlayerUnlessEnemyProtected(int, int);
309 static void KillPlayerUnlessExplosionProtected(int, int);
311 static void TestIfPlayerTouchesCustomElement(int, int);
312 static void TestIfElementTouchesCustomElement(int, int);
313 static void TestIfElementHitsCustomElement(int, int, int);
315 static void TestIfElementSmashesCustomElement(int, int, int);
318 static void HandleElementChange(int, int, int);
319 static void ExecuteCustomElementAction(int, int, int, int);
320 static boolean ChangeElement(int, int, int, int);
322 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
323 #define CheckTriggeredElementChange(x, y, e, ev) \
324 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
325 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
326 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
327 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
328 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
329 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
330 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
332 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
333 #define CheckElementChange(x, y, e, te, ev) \
334 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
335 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
336 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
337 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
338 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
340 static void PlayLevelSound(int, int, int);
341 static void PlayLevelSoundNearest(int, int, int);
342 static void PlayLevelSoundAction(int, int, int);
343 static void PlayLevelSoundElementAction(int, int, int, int);
344 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
345 static void PlayLevelSoundActionIfLoop(int, int, int);
346 static void StopLevelSoundActionIfLoop(int, int, int);
347 static void PlayLevelMusic();
349 static void MapGameButtons();
350 static void HandleGameButtons(struct GadgetInfo *);
352 int AmoebeNachbarNr(int, int);
353 void AmoebeUmwandeln(int, int);
354 void ContinueMoving(int, int);
356 void InitMovDir(int, int);
357 void InitAmoebaNr(int, int);
358 int NewHiScore(void);
360 void TestIfGoodThingHitsBadThing(int, int, int);
361 void TestIfBadThingHitsGoodThing(int, int, int);
362 void TestIfPlayerTouchesBadThing(int, int);
363 void TestIfPlayerRunsIntoBadThing(int, int, int);
364 void TestIfBadThingTouchesPlayer(int, int);
365 void TestIfBadThingRunsIntoPlayer(int, int, int);
366 void TestIfFriendTouchesBadThing(int, int);
367 void TestIfBadThingTouchesFriend(int, int);
368 void TestIfBadThingTouchesOtherBadThing(int, int);
370 void KillPlayer(struct PlayerInfo *);
371 void BuryPlayer(struct PlayerInfo *);
372 void RemovePlayer(struct PlayerInfo *);
374 boolean SnapField(struct PlayerInfo *, int, int);
375 boolean DropElement(struct PlayerInfo *);
377 static int getInvisibleActiveFromInvisibleElement(int);
378 static int getInvisibleFromInvisibleActiveElement(int);
380 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
383 /* ------------------------------------------------------------------------- */
384 /* definition of elements that automatically change to other elements after */
385 /* a specified time, eventually calling a function when changing */
386 /* ------------------------------------------------------------------------- */
388 /* forward declaration for changer functions */
389 static void InitBuggyBase(int, int);
390 static void WarnBuggyBase(int, int);
392 static void InitTrap(int, int);
393 static void ActivateTrap(int, int);
394 static void ChangeActiveTrap(int, int);
396 static void InitRobotWheel(int, int);
397 static void RunRobotWheel(int, int);
398 static void StopRobotWheel(int, int);
400 static void InitTimegateWheel(int, int);
401 static void RunTimegateWheel(int, int);
403 static void InitMagicBallDelay(int, int);
404 static void ActivateMagicBall(int, int);
406 struct ChangingElementInfo
411 void (*pre_change_function)(int x, int y);
412 void (*change_function)(int x, int y);
413 void (*post_change_function)(int x, int y);
416 static struct ChangingElementInfo change_delay_list[] =
467 EL_SWITCHGATE_OPENING,
475 EL_SWITCHGATE_CLOSING,
476 EL_SWITCHGATE_CLOSED,
508 EL_ACID_SPLASH_RIGHT,
517 EL_SP_BUGGY_BASE_ACTIVATING,
524 EL_SP_BUGGY_BASE_ACTIVATING,
525 EL_SP_BUGGY_BASE_ACTIVE,
532 EL_SP_BUGGY_BASE_ACTIVE,
556 EL_ROBOT_WHEEL_ACTIVE,
564 EL_TIMEGATE_SWITCH_ACTIVE,
572 EL_EMC_MAGIC_BALL_ACTIVE,
573 EL_EMC_MAGIC_BALL_ACTIVE,
580 EL_EMC_SPRING_BUMPER_ACTIVE,
581 EL_EMC_SPRING_BUMPER,
588 EL_DIAGONAL_SHRINKING,
617 int push_delay_fixed, push_delay_random;
622 { EL_BALLOON, 0, 0 },
624 { EL_SOKOBAN_OBJECT, 2, 0 },
625 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
626 { EL_SATELLITE, 2, 0 },
627 { EL_SP_DISK_YELLOW, 2, 0 },
629 { EL_UNDEFINED, 0, 0 },
637 move_stepsize_list[] =
639 { EL_AMOEBA_DROP, 2 },
640 { EL_AMOEBA_DROPPING, 2 },
641 { EL_QUICKSAND_FILLING, 1 },
642 { EL_QUICKSAND_EMPTYING, 1 },
643 { EL_MAGIC_WALL_FILLING, 2 },
644 { EL_BD_MAGIC_WALL_FILLING, 2 },
645 { EL_MAGIC_WALL_EMPTYING, 2 },
646 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
656 collect_count_list[] =
659 { EL_BD_DIAMOND, 1 },
660 { EL_EMERALD_YELLOW, 1 },
661 { EL_EMERALD_RED, 1 },
662 { EL_EMERALD_PURPLE, 1 },
664 { EL_SP_INFOTRON, 1 },
676 access_direction_list[] =
678 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
679 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
680 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
681 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
682 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
683 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
684 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
685 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
686 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
687 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
688 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
690 { EL_SP_PORT_LEFT, MV_RIGHT },
691 { EL_SP_PORT_RIGHT, MV_LEFT },
692 { EL_SP_PORT_UP, MV_DOWN },
693 { EL_SP_PORT_DOWN, MV_UP },
694 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
695 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
696 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
697 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
698 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
699 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
700 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
701 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
702 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
703 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
704 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
705 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
706 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
707 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
708 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
710 { EL_UNDEFINED, MV_NONE }
713 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
715 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
716 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
717 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
718 IS_JUST_CHANGING(x, y))
720 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
722 /* static variables for playfield scan mode (scanning forward or backward) */
723 static int playfield_scan_start_x = 0;
724 static int playfield_scan_start_y = 0;
725 static int playfield_scan_delta_x = 1;
726 static int playfield_scan_delta_y = 1;
728 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
729 (y) >= 0 && (y) <= lev_fieldy - 1; \
730 (y) += playfield_scan_delta_y) \
731 for ((x) = playfield_scan_start_x; \
732 (x) >= 0 && (x) <= lev_fieldx - 1; \
733 (x) += playfield_scan_delta_x) \
736 void DEBUG_SetMaximumDynamite()
740 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
741 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
742 local_player->inventory_element[local_player->inventory_size++] =
747 static void InitPlayfieldScanModeVars()
749 if (game.use_reverse_scan_direction)
751 playfield_scan_start_x = lev_fieldx - 1;
752 playfield_scan_start_y = lev_fieldy - 1;
754 playfield_scan_delta_x = -1;
755 playfield_scan_delta_y = -1;
759 playfield_scan_start_x = 0;
760 playfield_scan_start_y = 0;
762 playfield_scan_delta_x = 1;
763 playfield_scan_delta_y = 1;
767 static void InitPlayfieldScanMode(int mode)
769 game.use_reverse_scan_direction =
770 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
772 InitPlayfieldScanModeVars();
775 static int get_move_delay_from_stepsize(int move_stepsize)
778 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
780 /* make sure that stepsize value is always a power of 2 */
781 move_stepsize = (1 << log_2(move_stepsize));
783 return TILEX / move_stepsize;
786 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
789 int player_nr = player->index_nr;
790 int move_delay = get_move_delay_from_stepsize(move_stepsize);
791 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
793 /* do no immediately change move delay -- the player might just be moving */
794 player->move_delay_value_next = move_delay;
796 /* information if player can move must be set separately */
797 player->cannot_move = cannot_move;
801 player->move_delay = game.initial_move_delay[player_nr];
802 player->move_delay_value = game.initial_move_delay_value[player_nr];
804 player->move_delay_value_next = -1;
806 player->move_delay_reset_counter = 0;
810 void GetPlayerConfig()
812 if (!audio.sound_available)
813 setup.sound_simple = FALSE;
815 if (!audio.loops_available)
816 setup.sound_loops = FALSE;
818 if (!audio.music_available)
819 setup.sound_music = FALSE;
821 if (!video.fullscreen_available)
822 setup.fullscreen = FALSE;
824 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
826 SetAudioMode(setup.sound);
830 static int getBeltNrFromBeltElement(int element)
832 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
833 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
834 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
837 static int getBeltNrFromBeltActiveElement(int element)
839 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
840 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
841 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
844 static int getBeltNrFromBeltSwitchElement(int element)
846 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
847 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
848 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
851 static int getBeltDirNrFromBeltSwitchElement(int element)
853 static int belt_base_element[4] =
855 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
856 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
857 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
858 EL_CONVEYOR_BELT_4_SWITCH_LEFT
861 int belt_nr = getBeltNrFromBeltSwitchElement(element);
862 int belt_dir_nr = element - belt_base_element[belt_nr];
864 return (belt_dir_nr % 3);
867 static int getBeltDirFromBeltSwitchElement(int element)
869 static int belt_move_dir[3] =
876 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
878 return belt_move_dir[belt_dir_nr];
881 static int get_element_from_group_element(int element)
883 if (IS_GROUP_ELEMENT(element))
885 struct ElementGroupInfo *group = element_info[element].group;
886 int last_anim_random_frame = gfx.anim_random_frame;
889 if (group->choice_mode == ANIM_RANDOM)
890 gfx.anim_random_frame = RND(group->num_elements_resolved);
892 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
893 group->choice_mode, 0,
896 if (group->choice_mode == ANIM_RANDOM)
897 gfx.anim_random_frame = last_anim_random_frame;
901 element = group->element_resolved[element_pos];
907 static void InitPlayerField(int x, int y, int element, boolean init_game)
909 if (element == EL_SP_MURPHY)
913 if (stored_player[0].present)
915 Feld[x][y] = EL_SP_MURPHY_CLONE;
921 stored_player[0].use_murphy = TRUE;
923 if (!level.use_artwork_element[0])
924 stored_player[0].artwork_element = EL_SP_MURPHY;
927 Feld[x][y] = EL_PLAYER_1;
933 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
934 int jx = player->jx, jy = player->jy;
936 player->present = TRUE;
938 player->block_last_field = (element == EL_SP_MURPHY ?
939 level.sp_block_last_field :
940 level.block_last_field);
942 /* ---------- initialize player's last field block delay --------------- */
944 /* always start with reliable default value (no adjustment needed) */
945 player->block_delay_adjustment = 0;
947 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
948 if (player->block_last_field && element == EL_SP_MURPHY)
949 player->block_delay_adjustment = 1;
951 /* special case 2: in game engines before 3.1.1, blocking was different */
952 if (game.use_block_last_field_bug)
953 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
955 if (!options.network || player->connected)
957 player->active = TRUE;
959 /* remove potentially duplicate players */
960 if (StorePlayer[jx][jy] == Feld[x][y])
961 StorePlayer[jx][jy] = 0;
963 StorePlayer[x][y] = Feld[x][y];
967 printf("Player %d activated.\n", player->element_nr);
968 printf("[Local player is %d and currently %s.]\n",
969 local_player->element_nr,
970 local_player->active ? "active" : "not active");
974 Feld[x][y] = EL_EMPTY;
976 player->jx = player->last_jx = x;
977 player->jy = player->last_jy = y;
981 static void InitField(int x, int y, boolean init_game)
983 int element = Feld[x][y];
992 InitPlayerField(x, y, element, init_game);
995 case EL_SOKOBAN_FIELD_PLAYER:
996 element = Feld[x][y] = EL_PLAYER_1;
997 InitField(x, y, init_game);
999 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1000 InitField(x, y, init_game);
1003 case EL_SOKOBAN_FIELD_EMPTY:
1004 local_player->sokobanfields_still_needed++;
1008 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1009 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1010 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1011 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1012 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1013 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1014 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1015 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1016 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1017 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1026 case EL_SPACESHIP_RIGHT:
1027 case EL_SPACESHIP_UP:
1028 case EL_SPACESHIP_LEFT:
1029 case EL_SPACESHIP_DOWN:
1030 case EL_BD_BUTTERFLY:
1031 case EL_BD_BUTTERFLY_RIGHT:
1032 case EL_BD_BUTTERFLY_UP:
1033 case EL_BD_BUTTERFLY_LEFT:
1034 case EL_BD_BUTTERFLY_DOWN:
1036 case EL_BD_FIREFLY_RIGHT:
1037 case EL_BD_FIREFLY_UP:
1038 case EL_BD_FIREFLY_LEFT:
1039 case EL_BD_FIREFLY_DOWN:
1040 case EL_PACMAN_RIGHT:
1042 case EL_PACMAN_LEFT:
1043 case EL_PACMAN_DOWN:
1045 case EL_YAMYAM_LEFT:
1046 case EL_YAMYAM_RIGHT:
1048 case EL_YAMYAM_DOWN:
1049 case EL_DARK_YAMYAM:
1052 case EL_SP_SNIKSNAK:
1053 case EL_SP_ELECTRON:
1062 case EL_AMOEBA_FULL:
1067 case EL_AMOEBA_DROP:
1068 if (y == lev_fieldy - 1)
1070 Feld[x][y] = EL_AMOEBA_GROWING;
1071 Store[x][y] = EL_AMOEBA_WET;
1075 case EL_DYNAMITE_ACTIVE:
1076 case EL_SP_DISK_RED_ACTIVE:
1077 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1078 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1079 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1080 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1081 MovDelay[x][y] = 96;
1084 case EL_EM_DYNAMITE_ACTIVE:
1085 MovDelay[x][y] = 32;
1089 local_player->lights_still_needed++;
1093 local_player->friends_still_needed++;
1098 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1101 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1102 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1103 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1104 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1105 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1106 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1107 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1108 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1109 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1110 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1111 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1112 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1115 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1116 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1117 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1119 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1121 game.belt_dir[belt_nr] = belt_dir;
1122 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1124 else /* more than one switch -- set it like the first switch */
1126 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1131 #if !USE_BOTH_SWITCHGATE_SWITCHES
1132 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1134 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1138 case EL_LIGHT_SWITCH_ACTIVE:
1140 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1143 case EL_INVISIBLE_STEELWALL:
1144 case EL_INVISIBLE_WALL:
1145 case EL_INVISIBLE_SAND:
1146 if (game.light_time_left > 0 ||
1147 game.lenses_time_left > 0)
1148 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1151 case EL_EMC_MAGIC_BALL:
1152 if (game.ball_state)
1153 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1156 case EL_EMC_MAGIC_BALL_SWITCH:
1157 if (game.ball_state)
1158 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1162 if (IS_CUSTOM_ELEMENT(element))
1164 if (CAN_MOVE(element))
1167 #if USE_NEW_CUSTOM_VALUE
1168 if (!element_info[element].use_last_ce_value || init_game)
1169 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1172 else if (IS_GROUP_ELEMENT(element))
1174 Feld[x][y] = get_element_from_group_element(element);
1176 InitField(x, y, init_game);
1183 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1186 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1188 InitField(x, y, init_game);
1190 /* not needed to call InitMovDir() -- already done by InitField()! */
1191 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1192 CAN_MOVE(Feld[x][y]))
1196 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1198 int old_element = Feld[x][y];
1200 InitField(x, y, init_game);
1202 /* not needed to call InitMovDir() -- already done by InitField()! */
1203 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1204 CAN_MOVE(old_element) &&
1205 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1208 /* this case is in fact a combination of not less than three bugs:
1209 first, it calls InitMovDir() for elements that can move, although this is
1210 already done by InitField(); then, it checks the element that was at this
1211 field _before_ the call to InitField() (which can change it); lastly, it
1212 was not called for "mole with direction" elements, which were treated as
1213 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1217 inline void DrawGameValue_Emeralds(int value)
1219 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1221 if (PANEL_DEACTIVATED(game.panel.gems))
1224 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1227 inline void DrawGameValue_Dynamite(int value)
1229 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1231 if (PANEL_DEACTIVATED(game.panel.inventory))
1234 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1237 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1239 int base_key_graphic = EL_KEY_1;
1242 if (PANEL_DEACTIVATED(game.panel.keys))
1245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1246 base_key_graphic = EL_EM_KEY_1;
1248 /* currently only 4 of 8 possible keys are displayed */
1249 for (i = 0; i < STD_NUM_KEYS; i++)
1251 int x = XX_KEYS + i * MINI_TILEX;
1255 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1257 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1258 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1262 inline void DrawGameValue_Score(int value)
1264 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1266 if (PANEL_DEACTIVATED(game.panel.score))
1269 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1272 inline void DrawGameValue_Time(int value)
1274 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1275 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1277 if (PANEL_DEACTIVATED(game.panel.time))
1280 /* clear background if value just changed its size */
1281 if (value == 999 || value == 1000)
1282 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1285 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1287 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1290 inline void DrawGameValue_Level(int value)
1292 if (PANEL_DEACTIVATED(game.panel.level))
1296 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1298 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1301 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1304 int key[MAX_NUM_KEYS];
1307 /* prevent EM engine from updating time/score values parallel to GameWon() */
1308 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1309 local_player->LevelSolved)
1312 for (i = 0; i < MAX_NUM_KEYS; i++)
1313 key[i] = key_bits & (1 << i);
1315 DrawGameValue_Level(level_nr);
1317 DrawGameValue_Emeralds(emeralds);
1318 DrawGameValue_Dynamite(dynamite);
1319 DrawGameValue_Score(score);
1320 DrawGameValue_Time(time);
1322 DrawGameValue_Keys(key);
1325 void DrawGameDoorValues()
1327 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1328 int dynamite_value = 0;
1329 int score_value = (local_player->LevelSolved ? local_player->score_final :
1330 local_player->score);
1331 int gems_value = local_player->gems_still_needed;
1335 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1337 DrawGameDoorValues_EM();
1342 if (game.centered_player_nr == -1)
1344 for (i = 0; i < MAX_PLAYERS; i++)
1346 for (j = 0; j < MAX_NUM_KEYS; j++)
1347 if (stored_player[i].key[j])
1348 key_bits |= (1 << j);
1350 dynamite_value += stored_player[i].inventory_size;
1355 int player_nr = game.centered_player_nr;
1357 for (i = 0; i < MAX_NUM_KEYS; i++)
1358 if (stored_player[player_nr].key[i])
1359 key_bits |= (1 << i);
1361 dynamite_value = stored_player[player_nr].inventory_size;
1364 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1370 =============================================================================
1372 -----------------------------------------------------------------------------
1373 initialize game engine due to level / tape version number
1374 =============================================================================
1377 static void InitGameEngine()
1379 int i, j, k, l, x, y;
1381 /* set game engine from tape file when re-playing, else from level file */
1382 game.engine_version = (tape.playing ? tape.engine_version :
1383 level.game_version);
1385 /* ---------------------------------------------------------------------- */
1386 /* set flags for bugs and changes according to active game engine version */
1387 /* ---------------------------------------------------------------------- */
1390 Summary of bugfix/change:
1391 Fixed handling for custom elements that change when pushed by the player.
1393 Fixed/changed in version:
1397 Before 3.1.0, custom elements that "change when pushing" changed directly
1398 after the player started pushing them (until then handled in "DigField()").
1399 Since 3.1.0, these custom elements are not changed until the "pushing"
1400 move of the element is finished (now handled in "ContinueMoving()").
1402 Affected levels/tapes:
1403 The first condition is generally needed for all levels/tapes before version
1404 3.1.0, which might use the old behaviour before it was changed; known tapes
1405 that are affected are some tapes from the level set "Walpurgis Gardens" by
1407 The second condition is an exception from the above case and is needed for
1408 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1409 above (including some development versions of 3.1.0), but before it was
1410 known that this change would break tapes like the above and was fixed in
1411 3.1.1, so that the changed behaviour was active although the engine version
1412 while recording maybe was before 3.1.0. There is at least one tape that is
1413 affected by this exception, which is the tape for the one-level set "Bug
1414 Machine" by Juergen Bonhagen.
1417 game.use_change_when_pushing_bug =
1418 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1420 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1421 tape.game_version < VERSION_IDENT(3,1,1,0)));
1424 Summary of bugfix/change:
1425 Fixed handling for blocking the field the player leaves when moving.
1427 Fixed/changed in version:
1431 Before 3.1.1, when "block last field when moving" was enabled, the field
1432 the player is leaving when moving was blocked for the time of the move,
1433 and was directly unblocked afterwards. This resulted in the last field
1434 being blocked for exactly one less than the number of frames of one player
1435 move. Additionally, even when blocking was disabled, the last field was
1436 blocked for exactly one frame.
1437 Since 3.1.1, due to changes in player movement handling, the last field
1438 is not blocked at all when blocking is disabled. When blocking is enabled,
1439 the last field is blocked for exactly the number of frames of one player
1440 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1441 last field is blocked for exactly one more than the number of frames of
1444 Affected levels/tapes:
1445 (!!! yet to be determined -- probably many !!!)
1448 game.use_block_last_field_bug =
1449 (game.engine_version < VERSION_IDENT(3,1,1,0));
1452 Summary of bugfix/change:
1453 Changed behaviour of CE changes with multiple changes per single frame.
1455 Fixed/changed in version:
1459 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1460 This resulted in race conditions where CEs seem to behave strange in some
1461 situations (where triggered CE changes were just skipped because there was
1462 already a CE change on that tile in the playfield in that engine frame).
1463 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1464 (The number of changes per frame must be limited in any case, because else
1465 it is easily possible to define CE changes that would result in an infinite
1466 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1467 should be set large enough so that it would only be reached in cases where
1468 the corresponding CE change conditions run into a loop. Therefore, it seems
1469 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1470 maximal number of change pages for custom elements.)
1472 Affected levels/tapes:
1476 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1477 game.max_num_changes_per_frame = 1;
1479 game.max_num_changes_per_frame =
1480 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1483 /* ---------------------------------------------------------------------- */
1485 /* default scan direction: scan playfield from top/left to bottom/right */
1486 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1488 /* dynamically adjust element properties according to game engine version */
1489 InitElementPropertiesEngine(game.engine_version);
1492 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1493 printf(" tape version == %06d [%s] [file: %06d]\n",
1494 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1496 printf(" => game.engine_version == %06d\n", game.engine_version);
1499 /* ---------- initialize player's initial move delay --------------------- */
1501 /* dynamically adjust player properties according to level information */
1502 for (i = 0; i < MAX_PLAYERS; i++)
1503 game.initial_move_delay_value[i] =
1504 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1506 /* dynamically adjust player properties according to game engine version */
1507 for (i = 0; i < MAX_PLAYERS; i++)
1508 game.initial_move_delay[i] =
1509 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1510 game.initial_move_delay_value[i] : 0);
1512 /* ---------- initialize player's initial push delay --------------------- */
1514 /* dynamically adjust player properties according to game engine version */
1515 game.initial_push_delay_value =
1516 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1518 /* ---------- initialize changing elements ------------------------------- */
1520 /* initialize changing elements information */
1521 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1523 struct ElementInfo *ei = &element_info[i];
1525 /* this pointer might have been changed in the level editor */
1526 ei->change = &ei->change_page[0];
1528 if (!IS_CUSTOM_ELEMENT(i))
1530 ei->change->target_element = EL_EMPTY_SPACE;
1531 ei->change->delay_fixed = 0;
1532 ei->change->delay_random = 0;
1533 ei->change->delay_frames = 1;
1536 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1538 ei->has_change_event[j] = FALSE;
1540 ei->event_page_nr[j] = 0;
1541 ei->event_page[j] = &ei->change_page[0];
1545 /* add changing elements from pre-defined list */
1546 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1548 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1549 struct ElementInfo *ei = &element_info[ch_delay->element];
1551 ei->change->target_element = ch_delay->target_element;
1552 ei->change->delay_fixed = ch_delay->change_delay;
1554 ei->change->pre_change_function = ch_delay->pre_change_function;
1555 ei->change->change_function = ch_delay->change_function;
1556 ei->change->post_change_function = ch_delay->post_change_function;
1558 ei->change->can_change = TRUE;
1559 ei->change->can_change_or_has_action = TRUE;
1561 ei->has_change_event[CE_DELAY] = TRUE;
1563 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1564 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1567 /* ---------- initialize internal run-time variables ------------- */
1569 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1571 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1573 for (j = 0; j < ei->num_change_pages; j++)
1575 ei->change_page[j].can_change_or_has_action =
1576 (ei->change_page[j].can_change |
1577 ei->change_page[j].has_action);
1581 /* add change events from custom element configuration */
1582 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1584 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1586 for (j = 0; j < ei->num_change_pages; j++)
1588 if (!ei->change_page[j].can_change_or_has_action)
1591 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1593 /* only add event page for the first page found with this event */
1594 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1596 ei->has_change_event[k] = TRUE;
1598 ei->event_page_nr[k] = j;
1599 ei->event_page[k] = &ei->change_page[j];
1605 /* ---------- initialize run-time trigger player and element ------------- */
1607 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1609 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1611 for (j = 0; j < ei->num_change_pages; j++)
1613 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1614 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1615 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1616 ei->change_page[j].actual_trigger_ce_value = 0;
1617 ei->change_page[j].actual_trigger_ce_score = 0;
1621 /* ---------- initialize trigger events ---------------------------------- */
1623 /* initialize trigger events information */
1624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1625 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1626 trigger_events[i][j] = FALSE;
1628 /* add trigger events from element change event properties */
1629 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1631 struct ElementInfo *ei = &element_info[i];
1633 for (j = 0; j < ei->num_change_pages; j++)
1635 if (!ei->change_page[j].can_change_or_has_action)
1638 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1640 int trigger_element = ei->change_page[j].trigger_element;
1642 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1644 if (ei->change_page[j].has_event[k])
1646 if (IS_GROUP_ELEMENT(trigger_element))
1648 struct ElementGroupInfo *group =
1649 element_info[trigger_element].group;
1651 for (l = 0; l < group->num_elements_resolved; l++)
1652 trigger_events[group->element_resolved[l]][k] = TRUE;
1654 else if (trigger_element == EL_ANY_ELEMENT)
1655 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1656 trigger_events[l][k] = TRUE;
1658 trigger_events[trigger_element][k] = TRUE;
1665 /* ---------- initialize push delay -------------------------------------- */
1667 /* initialize push delay values to default */
1668 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1670 if (!IS_CUSTOM_ELEMENT(i))
1672 /* set default push delay values (corrected since version 3.0.7-1) */
1673 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1675 element_info[i].push_delay_fixed = 2;
1676 element_info[i].push_delay_random = 8;
1680 element_info[i].push_delay_fixed = 8;
1681 element_info[i].push_delay_random = 8;
1686 /* set push delay value for certain elements from pre-defined list */
1687 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1689 int e = push_delay_list[i].element;
1691 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1692 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1695 /* set push delay value for Supaplex elements for newer engine versions */
1696 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1698 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1700 if (IS_SP_ELEMENT(i))
1702 /* set SP push delay to just enough to push under a falling zonk */
1703 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1705 element_info[i].push_delay_fixed = delay;
1706 element_info[i].push_delay_random = 0;
1711 /* ---------- initialize move stepsize ----------------------------------- */
1713 /* initialize move stepsize values to default */
1714 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1715 if (!IS_CUSTOM_ELEMENT(i))
1716 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1718 /* set move stepsize value for certain elements from pre-defined list */
1719 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1721 int e = move_stepsize_list[i].element;
1723 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1726 /* ---------- initialize collect score ----------------------------------- */
1728 /* initialize collect score values for custom elements from initial value */
1729 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1730 if (IS_CUSTOM_ELEMENT(i))
1731 element_info[i].collect_score = element_info[i].collect_score_initial;
1733 /* ---------- initialize collect count ----------------------------------- */
1735 /* initialize collect count values for non-custom elements */
1736 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1737 if (!IS_CUSTOM_ELEMENT(i))
1738 element_info[i].collect_count_initial = 0;
1740 /* add collect count values for all elements from pre-defined list */
1741 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1742 element_info[collect_count_list[i].element].collect_count_initial =
1743 collect_count_list[i].count;
1745 /* ---------- initialize access direction -------------------------------- */
1747 /* initialize access direction values to default (access from every side) */
1748 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1749 if (!IS_CUSTOM_ELEMENT(i))
1750 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1752 /* set access direction value for certain elements from pre-defined list */
1753 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1754 element_info[access_direction_list[i].element].access_direction =
1755 access_direction_list[i].direction;
1757 /* ---------- initialize explosion content ------------------------------- */
1758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1760 if (IS_CUSTOM_ELEMENT(i))
1763 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1765 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1767 element_info[i].content.e[x][y] =
1768 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1769 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1770 i == EL_PLAYER_3 ? EL_EMERALD :
1771 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1772 i == EL_MOLE ? EL_EMERALD_RED :
1773 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1774 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1775 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1776 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1777 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1778 i == EL_WALL_EMERALD ? EL_EMERALD :
1779 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1780 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1781 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1782 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1783 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1784 i == EL_WALL_PEARL ? EL_PEARL :
1785 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1791 int get_num_special_action(int element, int action_first, int action_last)
1793 int num_special_action = 0;
1796 for (i = action_first; i <= action_last; i++)
1798 boolean found = FALSE;
1800 for (j = 0; j < NUM_DIRECTIONS; j++)
1801 if (el_act_dir2img(element, i, j) !=
1802 el_act_dir2img(element, ACTION_DEFAULT, j))
1806 num_special_action++;
1811 return num_special_action;
1816 =============================================================================
1818 -----------------------------------------------------------------------------
1819 initialize and start new game
1820 =============================================================================
1825 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1826 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1827 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1828 boolean do_fading = (game_status == GAME_MODE_MAIN);
1831 game_status = GAME_MODE_PLAYING;
1835 /* don't play tapes over network */
1836 network_playing = (options.network && !tape.playing);
1838 for (i = 0; i < MAX_PLAYERS; i++)
1840 struct PlayerInfo *player = &stored_player[i];
1842 player->index_nr = i;
1843 player->index_bit = (1 << i);
1844 player->element_nr = EL_PLAYER_1 + i;
1846 player->present = FALSE;
1847 player->active = FALSE;
1848 player->killed = FALSE;
1851 player->effective_action = 0;
1852 player->programmed_action = 0;
1855 player->score_final = 0;
1857 player->gems_still_needed = level.gems_needed;
1858 player->sokobanfields_still_needed = 0;
1859 player->lights_still_needed = 0;
1860 player->friends_still_needed = 0;
1862 for (j = 0; j < MAX_NUM_KEYS; j++)
1863 player->key[j] = FALSE;
1865 player->dynabomb_count = 0;
1866 player->dynabomb_size = 1;
1867 player->dynabombs_left = 0;
1868 player->dynabomb_xl = FALSE;
1870 player->MovDir = MV_NONE;
1873 player->GfxDir = MV_NONE;
1874 player->GfxAction = ACTION_DEFAULT;
1876 player->StepFrame = 0;
1878 player->use_murphy = FALSE;
1879 player->artwork_element =
1880 (level.use_artwork_element[i] ? level.artwork_element[i] :
1881 player->element_nr);
1883 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1884 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1886 player->gravity = level.initial_player_gravity[i];
1888 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1890 player->actual_frame_counter = 0;
1892 player->step_counter = 0;
1894 player->last_move_dir = MV_NONE;
1896 player->is_active = FALSE;
1898 player->is_waiting = FALSE;
1899 player->is_moving = FALSE;
1900 player->is_auto_moving = FALSE;
1901 player->is_digging = FALSE;
1902 player->is_snapping = FALSE;
1903 player->is_collecting = FALSE;
1904 player->is_pushing = FALSE;
1905 player->is_switching = FALSE;
1906 player->is_dropping = FALSE;
1907 player->is_dropping_pressed = FALSE;
1909 player->is_bored = FALSE;
1910 player->is_sleeping = FALSE;
1912 player->frame_counter_bored = -1;
1913 player->frame_counter_sleeping = -1;
1915 player->anim_delay_counter = 0;
1916 player->post_delay_counter = 0;
1918 player->dir_waiting = MV_NONE;
1919 player->action_waiting = ACTION_DEFAULT;
1920 player->last_action_waiting = ACTION_DEFAULT;
1921 player->special_action_bored = ACTION_DEFAULT;
1922 player->special_action_sleeping = ACTION_DEFAULT;
1924 player->switch_x = -1;
1925 player->switch_y = -1;
1927 player->drop_x = -1;
1928 player->drop_y = -1;
1930 player->show_envelope = 0;
1932 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1934 player->push_delay = -1; /* initialized when pushing starts */
1935 player->push_delay_value = game.initial_push_delay_value;
1937 player->drop_delay = 0;
1938 player->drop_pressed_delay = 0;
1940 player->last_jx = -1;
1941 player->last_jy = -1;
1945 player->shield_normal_time_left = 0;
1946 player->shield_deadly_time_left = 0;
1948 player->inventory_infinite_element = EL_UNDEFINED;
1949 player->inventory_size = 0;
1951 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1952 SnapField(player, 0, 0);
1954 player->LevelSolved = FALSE;
1955 player->GameOver = FALSE;
1957 player->LevelSolved_GameEnd = FALSE;
1958 player->LevelSolved_SaveTape = FALSE;
1959 player->LevelSolved_SaveScore = FALSE;
1962 network_player_action_received = FALSE;
1964 #if defined(NETWORK_AVALIABLE)
1965 /* initial null action */
1966 if (network_playing)
1967 SendToServer_MovePlayer(MV_NONE);
1976 TimeLeft = level.time;
1979 ScreenMovDir = MV_NONE;
1983 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1985 AllPlayersGone = FALSE;
1987 game.yamyam_content_nr = 0;
1988 game.magic_wall_active = FALSE;
1989 game.magic_wall_time_left = 0;
1990 game.light_time_left = 0;
1991 game.timegate_time_left = 0;
1992 game.switchgate_pos = 0;
1993 game.wind_direction = level.wind_direction_initial;
1995 #if !USE_PLAYER_GRAVITY
1996 game.gravity = FALSE;
1997 game.explosions_delayed = TRUE;
2000 game.lenses_time_left = 0;
2001 game.magnify_time_left = 0;
2003 game.ball_state = level.ball_state_initial;
2004 game.ball_content_nr = 0;
2006 game.envelope_active = FALSE;
2008 /* set focus to local player for network games, else to all players */
2009 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2010 game.centered_player_nr_next = game.centered_player_nr;
2011 game.set_centered_player = FALSE;
2013 if (network_playing && tape.recording)
2015 /* store client dependent player focus when recording network games */
2016 tape.centered_player_nr_next = game.centered_player_nr_next;
2017 tape.set_centered_player = TRUE;
2020 for (i = 0; i < NUM_BELTS; i++)
2022 game.belt_dir[i] = MV_NONE;
2023 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2026 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2027 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2029 SCAN_PLAYFIELD(x, y)
2031 Feld[x][y] = level.field[x][y];
2032 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2033 ChangeDelay[x][y] = 0;
2034 ChangePage[x][y] = -1;
2035 #if USE_NEW_CUSTOM_VALUE
2036 CustomValue[x][y] = 0; /* initialized in InitField() */
2038 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2040 WasJustMoving[x][y] = 0;
2041 WasJustFalling[x][y] = 0;
2042 CheckCollision[x][y] = 0;
2043 CheckImpact[x][y] = 0;
2045 Pushed[x][y] = FALSE;
2047 ChangeCount[x][y] = 0;
2048 ChangeEvent[x][y] = -1;
2050 ExplodePhase[x][y] = 0;
2051 ExplodeDelay[x][y] = 0;
2052 ExplodeField[x][y] = EX_TYPE_NONE;
2054 RunnerVisit[x][y] = 0;
2055 PlayerVisit[x][y] = 0;
2058 GfxRandom[x][y] = INIT_GFX_RANDOM();
2059 GfxElement[x][y] = EL_UNDEFINED;
2060 GfxAction[x][y] = ACTION_DEFAULT;
2061 GfxDir[x][y] = MV_NONE;
2064 SCAN_PLAYFIELD(x, y)
2066 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2068 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2070 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2073 InitField(x, y, TRUE);
2078 for (i = 0; i < MAX_PLAYERS; i++)
2080 struct PlayerInfo *player = &stored_player[i];
2082 /* set number of special actions for bored and sleeping animation */
2083 player->num_special_action_bored =
2084 get_num_special_action(player->artwork_element,
2085 ACTION_BORING_1, ACTION_BORING_LAST);
2086 player->num_special_action_sleeping =
2087 get_num_special_action(player->artwork_element,
2088 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2091 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2092 emulate_sb ? EMU_SOKOBAN :
2093 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2095 #if USE_NEW_ALL_SLIPPERY
2096 /* initialize type of slippery elements */
2097 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2099 if (!IS_CUSTOM_ELEMENT(i))
2101 /* default: elements slip down either to the left or right randomly */
2102 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2104 /* SP style elements prefer to slip down on the left side */
2105 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2106 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2108 /* BD style elements prefer to slip down on the left side */
2109 if (game.emulation == EMU_BOULDERDASH)
2110 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2115 /* initialize explosion and ignition delay */
2116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2118 if (!IS_CUSTOM_ELEMENT(i))
2121 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2122 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2123 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2124 int last_phase = (num_phase + 1) * delay;
2125 int half_phase = (num_phase / 2) * delay;
2127 element_info[i].explosion_delay = last_phase - 1;
2128 element_info[i].ignition_delay = half_phase;
2130 if (i == EL_BLACK_ORB)
2131 element_info[i].ignition_delay = 1;
2135 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2136 element_info[i].explosion_delay = 1;
2138 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2139 element_info[i].ignition_delay = 1;
2143 /* correct non-moving belts to start moving left */
2144 for (i = 0; i < NUM_BELTS; i++)
2145 if (game.belt_dir[i] == MV_NONE)
2146 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2148 /* check if any connected player was not found in playfield */
2149 for (i = 0; i < MAX_PLAYERS; i++)
2151 struct PlayerInfo *player = &stored_player[i];
2153 if (player->connected && !player->present)
2155 for (j = 0; j < MAX_PLAYERS; j++)
2157 struct PlayerInfo *some_player = &stored_player[j];
2158 int jx = some_player->jx, jy = some_player->jy;
2160 /* assign first free player found that is present in the playfield */
2161 if (some_player->present && !some_player->connected)
2163 player->present = TRUE;
2164 player->active = TRUE;
2166 some_player->present = FALSE;
2167 some_player->active = FALSE;
2169 player->artwork_element = some_player->artwork_element;
2171 player->block_last_field = some_player->block_last_field;
2172 player->block_delay_adjustment = some_player->block_delay_adjustment;
2174 StorePlayer[jx][jy] = player->element_nr;
2175 player->jx = player->last_jx = jx;
2176 player->jy = player->last_jy = jy;
2186 /* when playing a tape, eliminate all players who do not participate */
2188 for (i = 0; i < MAX_PLAYERS; i++)
2190 if (stored_player[i].active && !tape.player_participates[i])
2192 struct PlayerInfo *player = &stored_player[i];
2193 int jx = player->jx, jy = player->jy;
2195 player->active = FALSE;
2196 StorePlayer[jx][jy] = 0;
2197 Feld[jx][jy] = EL_EMPTY;
2201 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2203 /* when in single player mode, eliminate all but the first active player */
2205 for (i = 0; i < MAX_PLAYERS; i++)
2207 if (stored_player[i].active)
2209 for (j = i + 1; j < MAX_PLAYERS; j++)
2211 if (stored_player[j].active)
2213 struct PlayerInfo *player = &stored_player[j];
2214 int jx = player->jx, jy = player->jy;
2216 player->active = FALSE;
2217 player->present = FALSE;
2219 StorePlayer[jx][jy] = 0;
2220 Feld[jx][jy] = EL_EMPTY;
2227 /* when recording the game, store which players take part in the game */
2230 for (i = 0; i < MAX_PLAYERS; i++)
2231 if (stored_player[i].active)
2232 tape.player_participates[i] = TRUE;
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 struct PlayerInfo *player = &stored_player[i];
2241 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2246 if (local_player == player)
2247 printf("Player %d is local player.\n", i+1);
2251 if (BorderElement == EL_EMPTY)
2254 SBX_Right = lev_fieldx - SCR_FIELDX;
2256 SBY_Lower = lev_fieldy - SCR_FIELDY;
2261 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2263 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2266 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2267 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2269 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2270 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2272 /* if local player not found, look for custom element that might create
2273 the player (make some assumptions about the right custom element) */
2274 if (!local_player->present)
2276 int start_x = 0, start_y = 0;
2277 int found_rating = 0;
2278 int found_element = EL_UNDEFINED;
2279 int player_nr = local_player->index_nr;
2281 SCAN_PLAYFIELD(x, y)
2283 int element = Feld[x][y];
2288 if (level.use_start_element[player_nr] &&
2289 level.start_element[player_nr] == element &&
2296 found_element = element;
2299 if (!IS_CUSTOM_ELEMENT(element))
2302 if (CAN_CHANGE(element))
2304 for (i = 0; i < element_info[element].num_change_pages; i++)
2306 /* check for player created from custom element as single target */
2307 content = element_info[element].change_page[i].target_element;
2308 is_player = ELEM_IS_PLAYER(content);
2310 if (is_player && (found_rating < 3 || element < found_element))
2316 found_element = element;
2321 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2323 /* check for player created from custom element as explosion content */
2324 content = element_info[element].content.e[xx][yy];
2325 is_player = ELEM_IS_PLAYER(content);
2327 if (is_player && (found_rating < 2 || element < found_element))
2329 start_x = x + xx - 1;
2330 start_y = y + yy - 1;
2333 found_element = element;
2336 if (!CAN_CHANGE(element))
2339 for (i = 0; i < element_info[element].num_change_pages; i++)
2341 /* check for player created from custom element as extended target */
2343 element_info[element].change_page[i].target_content.e[xx][yy];
2345 is_player = ELEM_IS_PLAYER(content);
2347 if (is_player && (found_rating < 1 || element < found_element))
2349 start_x = x + xx - 1;
2350 start_y = y + yy - 1;
2353 found_element = element;
2359 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2360 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2363 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2364 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2369 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2370 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2371 local_player->jx - MIDPOSX);
2373 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2374 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2375 local_player->jy - MIDPOSY);
2380 if (!game.restart_level)
2381 CloseDoor(DOOR_CLOSE_1);
2384 FadeOut(REDRAW_FIELD);
2386 /* !!! FIX THIS (START) !!! */
2387 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2389 InitGameEngine_EM();
2391 /* blit playfield from scroll buffer to normal back buffer for fading in */
2392 BlitScreenToBitmap_EM(backbuffer);
2399 /* after drawing the level, correct some elements */
2400 if (game.timegate_time_left == 0)
2401 CloseAllOpenTimegates();
2403 /* blit playfield from scroll buffer to normal back buffer for fading in */
2404 if (setup.soft_scrolling)
2405 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2407 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2409 /* !!! FIX THIS (END) !!! */
2412 FadeIn(REDRAW_FIELD);
2416 if (!game.restart_level)
2418 /* copy default game door content to main double buffer */
2419 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2420 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2423 SetPanelBackground();
2424 SetDrawBackgroundMask(REDRAW_DOOR_1);
2426 DrawGameDoorValues();
2428 if (!game.restart_level)
2432 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2433 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2434 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2438 /* copy actual game door content to door double buffer for OpenDoor() */
2439 BlitBitmap(drawto, bitmap_db_door,
2440 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2442 OpenDoor(DOOR_OPEN_ALL);
2444 PlaySound(SND_GAME_STARTING);
2446 if (setup.sound_music)
2449 KeyboardAutoRepeatOffUnlessAutoplay();
2453 for (i = 0; i < MAX_PLAYERS; i++)
2454 printf("Player %d %sactive.\n",
2455 i + 1, (stored_player[i].active ? "" : "not "));
2466 game.restart_level = FALSE;
2469 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2471 /* this is used for non-R'n'D game engines to update certain engine values */
2473 /* needed to determine if sounds are played within the visible screen area */
2474 scroll_x = actual_scroll_x;
2475 scroll_y = actual_scroll_y;
2478 void InitMovDir(int x, int y)
2480 int i, element = Feld[x][y];
2481 static int xy[4][2] =
2488 static int direction[3][4] =
2490 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2491 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2492 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2501 Feld[x][y] = EL_BUG;
2502 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2505 case EL_SPACESHIP_RIGHT:
2506 case EL_SPACESHIP_UP:
2507 case EL_SPACESHIP_LEFT:
2508 case EL_SPACESHIP_DOWN:
2509 Feld[x][y] = EL_SPACESHIP;
2510 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2513 case EL_BD_BUTTERFLY_RIGHT:
2514 case EL_BD_BUTTERFLY_UP:
2515 case EL_BD_BUTTERFLY_LEFT:
2516 case EL_BD_BUTTERFLY_DOWN:
2517 Feld[x][y] = EL_BD_BUTTERFLY;
2518 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2521 case EL_BD_FIREFLY_RIGHT:
2522 case EL_BD_FIREFLY_UP:
2523 case EL_BD_FIREFLY_LEFT:
2524 case EL_BD_FIREFLY_DOWN:
2525 Feld[x][y] = EL_BD_FIREFLY;
2526 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2529 case EL_PACMAN_RIGHT:
2531 case EL_PACMAN_LEFT:
2532 case EL_PACMAN_DOWN:
2533 Feld[x][y] = EL_PACMAN;
2534 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2537 case EL_YAMYAM_LEFT:
2538 case EL_YAMYAM_RIGHT:
2540 case EL_YAMYAM_DOWN:
2541 Feld[x][y] = EL_YAMYAM;
2542 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2545 case EL_SP_SNIKSNAK:
2546 MovDir[x][y] = MV_UP;
2549 case EL_SP_ELECTRON:
2550 MovDir[x][y] = MV_LEFT;
2557 Feld[x][y] = EL_MOLE;
2558 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2562 if (IS_CUSTOM_ELEMENT(element))
2564 struct ElementInfo *ei = &element_info[element];
2565 int move_direction_initial = ei->move_direction_initial;
2566 int move_pattern = ei->move_pattern;
2568 if (move_direction_initial == MV_START_PREVIOUS)
2570 if (MovDir[x][y] != MV_NONE)
2573 move_direction_initial = MV_START_AUTOMATIC;
2576 if (move_direction_initial == MV_START_RANDOM)
2577 MovDir[x][y] = 1 << RND(4);
2578 else if (move_direction_initial & MV_ANY_DIRECTION)
2579 MovDir[x][y] = move_direction_initial;
2580 else if (move_pattern == MV_ALL_DIRECTIONS ||
2581 move_pattern == MV_TURNING_LEFT ||
2582 move_pattern == MV_TURNING_RIGHT ||
2583 move_pattern == MV_TURNING_LEFT_RIGHT ||
2584 move_pattern == MV_TURNING_RIGHT_LEFT ||
2585 move_pattern == MV_TURNING_RANDOM)
2586 MovDir[x][y] = 1 << RND(4);
2587 else if (move_pattern == MV_HORIZONTAL)
2588 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2589 else if (move_pattern == MV_VERTICAL)
2590 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2591 else if (move_pattern & MV_ANY_DIRECTION)
2592 MovDir[x][y] = element_info[element].move_pattern;
2593 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2594 move_pattern == MV_ALONG_RIGHT_SIDE)
2596 /* use random direction as default start direction */
2597 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2598 MovDir[x][y] = 1 << RND(4);
2600 for (i = 0; i < NUM_DIRECTIONS; i++)
2602 int x1 = x + xy[i][0];
2603 int y1 = y + xy[i][1];
2605 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2607 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2608 MovDir[x][y] = direction[0][i];
2610 MovDir[x][y] = direction[1][i];
2619 MovDir[x][y] = 1 << RND(4);
2621 if (element != EL_BUG &&
2622 element != EL_SPACESHIP &&
2623 element != EL_BD_BUTTERFLY &&
2624 element != EL_BD_FIREFLY)
2627 for (i = 0; i < NUM_DIRECTIONS; i++)
2629 int x1 = x + xy[i][0];
2630 int y1 = y + xy[i][1];
2632 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2634 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2636 MovDir[x][y] = direction[0][i];
2639 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2640 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2642 MovDir[x][y] = direction[1][i];
2651 GfxDir[x][y] = MovDir[x][y];
2654 void InitAmoebaNr(int x, int y)
2657 int group_nr = AmoebeNachbarNr(x, y);
2661 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2663 if (AmoebaCnt[i] == 0)
2671 AmoebaNr[x][y] = group_nr;
2672 AmoebaCnt[group_nr]++;
2673 AmoebaCnt2[group_nr]++;
2676 static void PlayerWins(struct PlayerInfo *player)
2678 player->LevelSolved = TRUE;
2679 player->GameOver = TRUE;
2681 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2682 level.native_em_level->lev->score : player->score);
2687 static int time, time_final;
2688 static int score, score_final;
2689 static int game_over_delay = 0;
2690 int game_over_delay_value = 50;
2692 if (!local_player->LevelSolved_GameEnd)
2696 /* do not start end game actions before the player stops moving (to exit) */
2697 if (local_player->MovPos)
2700 local_player->LevelSolved_GameEnd = TRUE;
2701 local_player->LevelSolved_SaveTape = tape.recording;
2702 local_player->LevelSolved_SaveScore = !tape.playing;
2704 if (tape.auto_play) /* tape might already be stopped here */
2705 tape.auto_play_level_solved = TRUE;
2711 game_over_delay = game_over_delay_value;
2713 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2714 score = score_final = local_player->score_final;
2719 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2721 else if (level.time == 0 && TimePlayed < 999)
2724 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2727 local_player->score_final = score_final;
2729 if (level_editor_test_game)
2732 score = score_final;
2734 DrawGameValue_Time(time);
2735 DrawGameValue_Score(score);
2738 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2740 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2742 /* close exit door after last player */
2743 if (AllPlayersGone &&
2744 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2745 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2747 int element = Feld[ExitX][ExitY];
2749 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2750 EL_SP_EXIT_CLOSING);
2752 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2755 /* player disappears */
2756 DrawLevelField(ExitX, ExitY);
2759 for (i = 0; i < MAX_PLAYERS; i++)
2761 struct PlayerInfo *player = &stored_player[i];
2763 if (player->present)
2765 RemovePlayer(player);
2767 /* player disappears */
2768 DrawLevelField(player->jx, player->jy);
2773 PlaySound(SND_GAME_WINNING);
2776 if (game_over_delay > 0)
2783 if (time != time_final)
2785 int time_to_go = ABS(time_final - time);
2786 int time_count_dir = (time < time_final ? +1 : -1);
2787 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2789 time += time_count_steps * time_count_dir;
2790 score += time_count_steps * level.score[SC_TIME_BONUS];
2792 DrawGameValue_Time(time);
2793 DrawGameValue_Score(score);
2795 if (time == time_final)
2796 StopSound(SND_GAME_LEVELTIME_BONUS);
2797 else if (setup.sound_loops)
2798 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2800 PlaySound(SND_GAME_LEVELTIME_BONUS);
2807 boolean raise_level = FALSE;
2809 CloseDoor(DOOR_CLOSE_1);
2811 if (local_player->LevelSolved_SaveTape)
2818 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2820 SaveTape(tape.level_nr); /* ask to save tape */
2824 if (level_editor_test_game)
2826 game_status = GAME_MODE_MAIN;
2833 if (!local_player->LevelSolved_SaveScore)
2835 FadeOut(REDRAW_FIELD);
2837 game_status = GAME_MODE_MAIN;
2839 DrawAndFadeInMainMenu(REDRAW_FIELD);
2844 if (level_nr == leveldir_current->handicap_level)
2846 leveldir_current->handicap_level++;
2847 SaveLevelSetup_SeriesInfo();
2850 if (level_nr < leveldir_current->last_level)
2851 raise_level = TRUE; /* advance to next level */
2853 if ((hi_pos = NewHiScore()) >= 0)
2855 game_status = GAME_MODE_SCORES;
2857 DrawHallOfFame(hi_pos);
2867 FadeOut(REDRAW_FIELD);
2869 game_status = GAME_MODE_MAIN;
2877 DrawAndFadeInMainMenu(REDRAW_FIELD);
2886 LoadScore(level_nr);
2888 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2889 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2892 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2894 if (local_player->score_final > highscore[k].Score)
2896 /* player has made it to the hall of fame */
2898 if (k < MAX_SCORE_ENTRIES - 1)
2900 int m = MAX_SCORE_ENTRIES - 1;
2903 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2904 if (strEqual(setup.player_name, highscore[l].Name))
2906 if (m == k) /* player's new highscore overwrites his old one */
2910 for (l = m; l > k; l--)
2912 strcpy(highscore[l].Name, highscore[l - 1].Name);
2913 highscore[l].Score = highscore[l - 1].Score;
2920 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2921 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2922 highscore[k].Score = local_player->score_final;
2928 else if (!strncmp(setup.player_name, highscore[k].Name,
2929 MAX_PLAYER_NAME_LEN))
2930 break; /* player already there with a higher score */
2936 SaveScore(level_nr);
2941 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2943 int element = Feld[x][y];
2944 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2945 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2946 int horiz_move = (dx != 0);
2947 int sign = (horiz_move ? dx : dy);
2948 int step = sign * element_info[element].move_stepsize;
2950 /* special values for move stepsize for spring and things on conveyor belt */
2953 if (CAN_FALL(element) &&
2954 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2955 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2956 else if (element == EL_SPRING)
2957 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2963 inline static int getElementMoveStepsize(int x, int y)
2965 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
2968 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2970 if (player->GfxAction != action || player->GfxDir != dir)
2973 printf("Player frame reset! (%d => %d, %d => %d)\n",
2974 player->GfxAction, action, player->GfxDir, dir);
2977 player->GfxAction = action;
2978 player->GfxDir = dir;
2980 player->StepFrame = 0;
2984 #if USE_GFX_RESET_GFX_ANIMATION
2985 static void ResetGfxFrame(int x, int y, boolean redraw)
2987 int element = Feld[x][y];
2988 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2989 int last_gfx_frame = GfxFrame[x][y];
2991 if (graphic_info[graphic].anim_global_sync)
2992 GfxFrame[x][y] = FrameCounter;
2993 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2994 GfxFrame[x][y] = CustomValue[x][y];
2995 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2996 GfxFrame[x][y] = element_info[element].collect_score;
2997 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
2998 GfxFrame[x][y] = ChangeDelay[x][y];
3000 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3001 DrawLevelGraphicAnimation(x, y, graphic);
3005 static void ResetGfxAnimation(int x, int y)
3007 GfxAction[x][y] = ACTION_DEFAULT;
3008 GfxDir[x][y] = MovDir[x][y];
3011 #if USE_GFX_RESET_GFX_ANIMATION
3012 ResetGfxFrame(x, y, FALSE);
3016 static void ResetRandomAnimationValue(int x, int y)
3018 GfxRandom[x][y] = INIT_GFX_RANDOM();
3021 void InitMovingField(int x, int y, int direction)
3023 int element = Feld[x][y];
3024 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3025 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3028 boolean is_moving_before, is_moving_after;
3030 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3033 /* check if element was/is moving or being moved before/after mode change */
3035 is_moving_before = WasJustMoving[x][y];
3037 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3039 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3041 /* reset animation only for moving elements which change direction of moving
3042 or which just started or stopped moving
3043 (else CEs with property "can move" / "not moving" are reset each frame) */
3044 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3046 if (is_moving_before != is_moving_after ||
3047 direction != MovDir[x][y])
3048 ResetGfxAnimation(x, y);
3050 if ((is_moving_before || is_moving_after) && !continues_moving)
3051 ResetGfxAnimation(x, y);
3054 if (!continues_moving)
3055 ResetGfxAnimation(x, y);
3058 MovDir[x][y] = direction;
3059 GfxDir[x][y] = direction;
3061 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3062 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3063 direction == MV_DOWN && CAN_FALL(element) ?
3064 ACTION_FALLING : ACTION_MOVING);
3066 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3067 ACTION_FALLING : ACTION_MOVING);
3070 /* this is needed for CEs with property "can move" / "not moving" */
3072 if (is_moving_after)
3074 if (Feld[newx][newy] == EL_EMPTY)
3075 Feld[newx][newy] = EL_BLOCKED;
3077 MovDir[newx][newy] = MovDir[x][y];
3079 #if USE_NEW_CUSTOM_VALUE
3080 CustomValue[newx][newy] = CustomValue[x][y];
3083 GfxFrame[newx][newy] = GfxFrame[x][y];
3084 GfxRandom[newx][newy] = GfxRandom[x][y];
3085 GfxAction[newx][newy] = GfxAction[x][y];
3086 GfxDir[newx][newy] = GfxDir[x][y];
3090 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3092 int direction = MovDir[x][y];
3093 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3094 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3100 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3102 int oldx = x, oldy = y;
3103 int direction = MovDir[x][y];
3105 if (direction == MV_LEFT)
3107 else if (direction == MV_RIGHT)
3109 else if (direction == MV_UP)
3111 else if (direction == MV_DOWN)
3114 *comes_from_x = oldx;
3115 *comes_from_y = oldy;
3118 int MovingOrBlocked2Element(int x, int y)
3120 int element = Feld[x][y];
3122 if (element == EL_BLOCKED)
3126 Blocked2Moving(x, y, &oldx, &oldy);
3127 return Feld[oldx][oldy];
3133 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3135 /* like MovingOrBlocked2Element(), but if element is moving
3136 and (x,y) is the field the moving element is just leaving,
3137 return EL_BLOCKED instead of the element value */
3138 int element = Feld[x][y];
3140 if (IS_MOVING(x, y))
3142 if (element == EL_BLOCKED)
3146 Blocked2Moving(x, y, &oldx, &oldy);
3147 return Feld[oldx][oldy];
3156 static void RemoveField(int x, int y)
3158 Feld[x][y] = EL_EMPTY;
3164 #if USE_NEW_CUSTOM_VALUE
3165 CustomValue[x][y] = 0;
3169 ChangeDelay[x][y] = 0;
3170 ChangePage[x][y] = -1;
3171 Pushed[x][y] = FALSE;
3174 ExplodeField[x][y] = EX_TYPE_NONE;
3177 GfxElement[x][y] = EL_UNDEFINED;
3178 GfxAction[x][y] = ACTION_DEFAULT;
3179 GfxDir[x][y] = MV_NONE;
3182 void RemoveMovingField(int x, int y)
3184 int oldx = x, oldy = y, newx = x, newy = y;
3185 int element = Feld[x][y];
3186 int next_element = EL_UNDEFINED;
3188 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3191 if (IS_MOVING(x, y))
3193 Moving2Blocked(x, y, &newx, &newy);
3195 if (Feld[newx][newy] != EL_BLOCKED)
3197 /* element is moving, but target field is not free (blocked), but
3198 already occupied by something different (example: acid pool);
3199 in this case, only remove the moving field, but not the target */
3201 RemoveField(oldx, oldy);
3203 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3205 DrawLevelField(oldx, oldy);
3210 else if (element == EL_BLOCKED)
3212 Blocked2Moving(x, y, &oldx, &oldy);
3213 if (!IS_MOVING(oldx, oldy))
3217 if (element == EL_BLOCKED &&
3218 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3219 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3220 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3221 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3222 next_element = get_next_element(Feld[oldx][oldy]);
3224 RemoveField(oldx, oldy);
3225 RemoveField(newx, newy);
3227 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3229 if (next_element != EL_UNDEFINED)
3230 Feld[oldx][oldy] = next_element;
3232 DrawLevelField(oldx, oldy);
3233 DrawLevelField(newx, newy);
3236 void DrawDynamite(int x, int y)
3238 int sx = SCREENX(x), sy = SCREENY(y);
3239 int graphic = el2img(Feld[x][y]);
3242 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3245 if (IS_WALKABLE_INSIDE(Back[x][y]))
3249 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3250 else if (Store[x][y])
3251 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3253 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3255 if (Back[x][y] || Store[x][y])
3256 DrawGraphicThruMask(sx, sy, graphic, frame);
3258 DrawGraphic(sx, sy, graphic, frame);
3261 void CheckDynamite(int x, int y)
3263 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3267 if (MovDelay[x][y] != 0)
3270 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3276 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3281 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3283 boolean num_checked_players = 0;
3286 for (i = 0; i < MAX_PLAYERS; i++)
3288 if (stored_player[i].active)
3290 int sx = stored_player[i].jx;
3291 int sy = stored_player[i].jy;
3293 if (num_checked_players == 0)
3300 *sx1 = MIN(*sx1, sx);
3301 *sy1 = MIN(*sy1, sy);
3302 *sx2 = MAX(*sx2, sx);
3303 *sy2 = MAX(*sy2, sy);
3306 num_checked_players++;
3311 static boolean checkIfAllPlayersFitToScreen_RND()
3313 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3315 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3317 return (sx2 - sx1 < SCR_FIELDX &&
3318 sy2 - sy1 < SCR_FIELDY);
3321 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3323 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3325 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3327 *sx = (sx1 + sx2) / 2;
3328 *sy = (sy1 + sy2) / 2;
3331 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3332 boolean center_screen, boolean quick_relocation)
3334 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3335 boolean no_delay = (tape.warp_forward);
3336 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3337 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3339 if (quick_relocation)
3341 int offset = (setup.scroll_delay ? 3 : 0);
3343 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3347 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3348 x > SBX_Right + MIDPOSX ? SBX_Right :
3351 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3352 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3357 /* quick relocation (without scrolling), but do not center screen */
3359 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3360 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3363 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3364 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3367 int offset_x = x + (scroll_x - center_scroll_x);
3368 int offset_y = y + (scroll_y - center_scroll_y);
3370 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3371 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3372 offset_x - MIDPOSX);
3374 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3375 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3376 offset_y - MIDPOSY);
3381 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3382 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3383 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3385 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3386 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3387 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3389 /* don't scroll over playfield boundaries */
3390 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3391 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3393 /* don't scroll over playfield boundaries */
3394 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3395 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3398 RedrawPlayfield(TRUE, 0,0,0,0);
3402 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3403 x > SBX_Right + MIDPOSX ? SBX_Right :
3406 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3407 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3410 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3412 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3415 int fx = FX, fy = FY;
3417 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3418 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3420 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3426 fx += dx * TILEX / 2;
3427 fy += dy * TILEY / 2;
3429 ScrollLevel(dx, dy);
3432 /* scroll in two steps of half tile size to make things smoother */
3433 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3435 Delay(wait_delay_value);
3437 /* scroll second step to align at full tile size */
3439 Delay(wait_delay_value);
3444 Delay(wait_delay_value);
3448 void RelocatePlayer(int jx, int jy, int el_player_raw)
3450 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3451 int player_nr = GET_PLAYER_NR(el_player);
3452 struct PlayerInfo *player = &stored_player[player_nr];
3453 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3454 boolean no_delay = (tape.warp_forward);
3455 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3456 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3457 int old_jx = player->jx;
3458 int old_jy = player->jy;
3459 int old_element = Feld[old_jx][old_jy];
3460 int element = Feld[jx][jy];
3461 boolean player_relocated = (old_jx != jx || old_jy != jy);
3463 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3464 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3465 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3466 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3467 int leave_side_horiz = move_dir_horiz;
3468 int leave_side_vert = move_dir_vert;
3469 int enter_side = enter_side_horiz | enter_side_vert;
3470 int leave_side = leave_side_horiz | leave_side_vert;
3472 if (player->GameOver) /* do not reanimate dead player */
3475 if (!player_relocated) /* no need to relocate the player */
3478 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3480 RemoveField(jx, jy); /* temporarily remove newly placed player */
3481 DrawLevelField(jx, jy);
3484 if (player->present)
3486 while (player->MovPos)
3488 ScrollPlayer(player, SCROLL_GO_ON);
3489 ScrollScreen(NULL, SCROLL_GO_ON);
3491 AdvanceFrameAndPlayerCounters(player->index_nr);
3496 Delay(wait_delay_value);
3499 DrawPlayer(player); /* needed here only to cleanup last field */
3500 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3502 player->is_moving = FALSE;
3505 if (IS_CUSTOM_ELEMENT(old_element))
3506 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3508 player->index_bit, leave_side);
3510 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3512 player->index_bit, leave_side);
3514 Feld[jx][jy] = el_player;
3515 InitPlayerField(jx, jy, el_player, TRUE);
3517 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3519 Feld[jx][jy] = element;
3520 InitField(jx, jy, FALSE);
3523 /* only visually relocate centered player */
3524 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3525 FALSE, level.instant_relocation);
3527 TestIfPlayerTouchesBadThing(jx, jy);
3528 TestIfPlayerTouchesCustomElement(jx, jy);
3530 if (IS_CUSTOM_ELEMENT(element))
3531 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3532 player->index_bit, enter_side);
3534 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3535 player->index_bit, enter_side);
3538 void Explode(int ex, int ey, int phase, int mode)
3544 /* !!! eliminate this variable !!! */
3545 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3547 if (game.explosions_delayed)
3549 ExplodeField[ex][ey] = mode;
3553 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3555 int center_element = Feld[ex][ey];
3556 int artwork_element, explosion_element; /* set these values later */
3559 /* --- This is only really needed (and now handled) in "Impact()". --- */
3560 /* do not explode moving elements that left the explode field in time */
3561 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3562 center_element == EL_EMPTY &&
3563 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3568 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3569 if (mode == EX_TYPE_NORMAL ||
3570 mode == EX_TYPE_CENTER ||
3571 mode == EX_TYPE_CROSS)
3572 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3575 /* remove things displayed in background while burning dynamite */
3576 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3579 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3581 /* put moving element to center field (and let it explode there) */
3582 center_element = MovingOrBlocked2Element(ex, ey);
3583 RemoveMovingField(ex, ey);
3584 Feld[ex][ey] = center_element;
3587 /* now "center_element" is finally determined -- set related values now */
3588 artwork_element = center_element; /* for custom player artwork */
3589 explosion_element = center_element; /* for custom player artwork */
3591 if (IS_PLAYER(ex, ey))
3593 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3595 artwork_element = stored_player[player_nr].artwork_element;
3597 if (level.use_explosion_element[player_nr])
3599 explosion_element = level.explosion_element[player_nr];
3600 artwork_element = explosion_element;
3605 if (mode == EX_TYPE_NORMAL ||
3606 mode == EX_TYPE_CENTER ||
3607 mode == EX_TYPE_CROSS)
3608 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3611 last_phase = element_info[explosion_element].explosion_delay + 1;
3613 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3615 int xx = x - ex + 1;
3616 int yy = y - ey + 1;
3619 if (!IN_LEV_FIELD(x, y) ||
3620 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3621 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3624 element = Feld[x][y];
3626 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3628 element = MovingOrBlocked2Element(x, y);
3630 if (!IS_EXPLOSION_PROOF(element))
3631 RemoveMovingField(x, y);
3634 /* indestructible elements can only explode in center (but not flames) */
3635 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3636 mode == EX_TYPE_BORDER)) ||
3637 element == EL_FLAMES)
3640 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3641 behaviour, for example when touching a yamyam that explodes to rocks
3642 with active deadly shield, a rock is created under the player !!! */
3643 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3645 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3646 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3647 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3649 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3652 if (IS_ACTIVE_BOMB(element))
3654 /* re-activate things under the bomb like gate or penguin */
3655 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3662 /* save walkable background elements while explosion on same tile */
3663 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3664 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3665 Back[x][y] = element;
3667 /* ignite explodable elements reached by other explosion */
3668 if (element == EL_EXPLOSION)
3669 element = Store2[x][y];
3671 if (AmoebaNr[x][y] &&
3672 (element == EL_AMOEBA_FULL ||
3673 element == EL_BD_AMOEBA ||
3674 element == EL_AMOEBA_GROWING))
3676 AmoebaCnt[AmoebaNr[x][y]]--;
3677 AmoebaCnt2[AmoebaNr[x][y]]--;
3682 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3684 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3686 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3688 if (PLAYERINFO(ex, ey)->use_murphy)
3689 Store[x][y] = EL_EMPTY;
3692 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3693 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3694 else if (ELEM_IS_PLAYER(center_element))
3695 Store[x][y] = EL_EMPTY;
3696 else if (center_element == EL_YAMYAM)
3697 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3698 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3699 Store[x][y] = element_info[center_element].content.e[xx][yy];
3701 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3702 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3703 otherwise) -- FIX THIS !!! */
3704 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3705 Store[x][y] = element_info[element].content.e[1][1];
3707 else if (!CAN_EXPLODE(element))
3708 Store[x][y] = element_info[element].content.e[1][1];
3711 Store[x][y] = EL_EMPTY;
3713 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3714 center_element == EL_AMOEBA_TO_DIAMOND)
3715 Store2[x][y] = element;
3717 Feld[x][y] = EL_EXPLOSION;
3718 GfxElement[x][y] = artwork_element;
3720 ExplodePhase[x][y] = 1;
3721 ExplodeDelay[x][y] = last_phase;
3726 if (center_element == EL_YAMYAM)
3727 game.yamyam_content_nr =
3728 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3740 GfxFrame[x][y] = 0; /* restart explosion animation */
3742 last_phase = ExplodeDelay[x][y];
3744 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3748 /* activate this even in non-DEBUG version until cause for crash in
3749 getGraphicAnimationFrame() (see below) is found and eliminated */
3755 /* this can happen if the player leaves an explosion just in time */
3756 if (GfxElement[x][y] == EL_UNDEFINED)
3757 GfxElement[x][y] = EL_EMPTY;
3759 if (GfxElement[x][y] == EL_UNDEFINED)
3762 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3763 printf("Explode(): This should never happen!\n");
3766 GfxElement[x][y] = EL_EMPTY;
3772 border_element = Store2[x][y];
3773 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3774 border_element = StorePlayer[x][y];
3776 if (phase == element_info[border_element].ignition_delay ||
3777 phase == last_phase)
3779 boolean border_explosion = FALSE;
3781 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3782 !PLAYER_EXPLOSION_PROTECTED(x, y))
3784 KillPlayerUnlessExplosionProtected(x, y);
3785 border_explosion = TRUE;
3787 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3789 Feld[x][y] = Store2[x][y];
3792 border_explosion = TRUE;
3794 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3796 AmoebeUmwandeln(x, y);
3798 border_explosion = TRUE;
3801 /* if an element just explodes due to another explosion (chain-reaction),
3802 do not immediately end the new explosion when it was the last frame of
3803 the explosion (as it would be done in the following "if"-statement!) */
3804 if (border_explosion && phase == last_phase)
3808 if (phase == last_phase)
3812 element = Feld[x][y] = Store[x][y];
3813 Store[x][y] = Store2[x][y] = 0;
3814 GfxElement[x][y] = EL_UNDEFINED;
3816 /* player can escape from explosions and might therefore be still alive */
3817 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3818 element <= EL_PLAYER_IS_EXPLODING_4)
3820 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3821 int explosion_element = EL_PLAYER_1 + player_nr;
3822 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3823 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3825 if (level.use_explosion_element[player_nr])
3826 explosion_element = level.explosion_element[player_nr];
3828 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3829 element_info[explosion_element].content.e[xx][yy]);
3832 /* restore probably existing indestructible background element */
3833 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3834 element = Feld[x][y] = Back[x][y];
3837 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3838 GfxDir[x][y] = MV_NONE;
3839 ChangeDelay[x][y] = 0;
3840 ChangePage[x][y] = -1;
3842 #if USE_NEW_CUSTOM_VALUE
3843 CustomValue[x][y] = 0;
3846 InitField_WithBug2(x, y, FALSE);
3848 DrawLevelField(x, y);
3850 TestIfElementTouchesCustomElement(x, y);
3852 if (GFX_CRUMBLED(element))
3853 DrawLevelFieldCrumbledSandNeighbours(x, y);
3855 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3856 StorePlayer[x][y] = 0;
3858 if (ELEM_IS_PLAYER(element))
3859 RelocatePlayer(x, y, element);
3861 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3863 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3864 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3867 DrawLevelFieldCrumbledSand(x, y);
3869 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3871 DrawLevelElement(x, y, Back[x][y]);
3872 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3874 else if (IS_WALKABLE_UNDER(Back[x][y]))
3876 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3877 DrawLevelElementThruMask(x, y, Back[x][y]);
3879 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3880 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3884 void DynaExplode(int ex, int ey)
3887 int dynabomb_element = Feld[ex][ey];
3888 int dynabomb_size = 1;
3889 boolean dynabomb_xl = FALSE;
3890 struct PlayerInfo *player;
3891 static int xy[4][2] =
3899 if (IS_ACTIVE_BOMB(dynabomb_element))
3901 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3902 dynabomb_size = player->dynabomb_size;
3903 dynabomb_xl = player->dynabomb_xl;
3904 player->dynabombs_left++;
3907 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3909 for (i = 0; i < NUM_DIRECTIONS; i++)
3911 for (j = 1; j <= dynabomb_size; j++)
3913 int x = ex + j * xy[i][0];
3914 int y = ey + j * xy[i][1];
3917 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3920 element = Feld[x][y];
3922 /* do not restart explosions of fields with active bombs */
3923 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3926 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3928 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3929 !IS_DIGGABLE(element) && !dynabomb_xl)
3935 void Bang(int x, int y)
3937 int element = MovingOrBlocked2Element(x, y);
3938 int explosion_type = EX_TYPE_NORMAL;
3940 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3942 struct PlayerInfo *player = PLAYERINFO(x, y);
3944 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3945 player->element_nr);
3947 if (level.use_explosion_element[player->index_nr])
3949 int explosion_element = level.explosion_element[player->index_nr];
3951 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3952 explosion_type = EX_TYPE_CROSS;
3953 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3954 explosion_type = EX_TYPE_CENTER;
3962 case EL_BD_BUTTERFLY:
3965 case EL_DARK_YAMYAM:
3969 RaiseScoreElement(element);
3972 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3973 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3974 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3975 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3976 case EL_DYNABOMB_INCREASE_NUMBER:
3977 case EL_DYNABOMB_INCREASE_SIZE:
3978 case EL_DYNABOMB_INCREASE_POWER:
3979 explosion_type = EX_TYPE_DYNA;
3984 case EL_LAMP_ACTIVE:
3985 case EL_AMOEBA_TO_DIAMOND:
3986 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3987 explosion_type = EX_TYPE_CENTER;
3991 if (element_info[element].explosion_type == EXPLODES_CROSS)
3992 explosion_type = EX_TYPE_CROSS;
3993 else if (element_info[element].explosion_type == EXPLODES_1X1)
3994 explosion_type = EX_TYPE_CENTER;
3998 if (explosion_type == EX_TYPE_DYNA)
4001 Explode(x, y, EX_PHASE_START, explosion_type);
4003 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4006 void SplashAcid(int x, int y)
4008 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4009 (!IN_LEV_FIELD(x - 1, y - 2) ||
4010 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4011 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4013 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4014 (!IN_LEV_FIELD(x + 1, y - 2) ||
4015 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4016 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4018 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4021 static void InitBeltMovement()
4023 static int belt_base_element[4] =
4025 EL_CONVEYOR_BELT_1_LEFT,
4026 EL_CONVEYOR_BELT_2_LEFT,
4027 EL_CONVEYOR_BELT_3_LEFT,
4028 EL_CONVEYOR_BELT_4_LEFT
4030 static int belt_base_active_element[4] =
4032 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4033 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4034 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4035 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4040 /* set frame order for belt animation graphic according to belt direction */
4041 for (i = 0; i < NUM_BELTS; i++)
4045 for (j = 0; j < NUM_BELT_PARTS; j++)
4047 int element = belt_base_active_element[belt_nr] + j;
4048 int graphic = el2img(element);
4050 if (game.belt_dir[i] == MV_LEFT)
4051 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4053 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4057 SCAN_PLAYFIELD(x, y)
4059 int element = Feld[x][y];
4061 for (i = 0; i < NUM_BELTS; i++)
4063 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4065 int e_belt_nr = getBeltNrFromBeltElement(element);
4068 if (e_belt_nr == belt_nr)
4070 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4072 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4079 static void ToggleBeltSwitch(int x, int y)
4081 static int belt_base_element[4] =
4083 EL_CONVEYOR_BELT_1_LEFT,
4084 EL_CONVEYOR_BELT_2_LEFT,
4085 EL_CONVEYOR_BELT_3_LEFT,
4086 EL_CONVEYOR_BELT_4_LEFT
4088 static int belt_base_active_element[4] =
4090 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4091 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4092 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4093 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4095 static int belt_base_switch_element[4] =
4097 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4098 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4099 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4100 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4102 static int belt_move_dir[4] =
4110 int element = Feld[x][y];
4111 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4112 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4113 int belt_dir = belt_move_dir[belt_dir_nr];
4116 if (!IS_BELT_SWITCH(element))
4119 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4120 game.belt_dir[belt_nr] = belt_dir;
4122 if (belt_dir_nr == 3)
4125 /* set frame order for belt animation graphic according to belt direction */
4126 for (i = 0; i < NUM_BELT_PARTS; i++)
4128 int element = belt_base_active_element[belt_nr] + i;
4129 int graphic = el2img(element);
4131 if (belt_dir == MV_LEFT)
4132 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4134 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4137 SCAN_PLAYFIELD(xx, yy)
4139 int element = Feld[xx][yy];
4141 if (IS_BELT_SWITCH(element))
4143 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4145 if (e_belt_nr == belt_nr)
4147 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4148 DrawLevelField(xx, yy);
4151 else if (IS_BELT(element) && belt_dir != MV_NONE)
4153 int e_belt_nr = getBeltNrFromBeltElement(element);
4155 if (e_belt_nr == belt_nr)
4157 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4159 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4160 DrawLevelField(xx, yy);
4163 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4165 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4167 if (e_belt_nr == belt_nr)
4169 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4171 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4172 DrawLevelField(xx, yy);
4178 static void ToggleSwitchgateSwitch(int x, int y)
4182 game.switchgate_pos = !game.switchgate_pos;
4184 SCAN_PLAYFIELD(xx, yy)
4186 int element = Feld[xx][yy];
4188 #if !USE_BOTH_SWITCHGATE_SWITCHES
4189 if (element == EL_SWITCHGATE_SWITCH_UP ||
4190 element == EL_SWITCHGATE_SWITCH_DOWN)
4192 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4193 DrawLevelField(xx, yy);
4196 if (element == EL_SWITCHGATE_SWITCH_UP)
4198 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4199 DrawLevelField(xx, yy);
4201 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4203 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4204 DrawLevelField(xx, yy);
4207 else if (element == EL_SWITCHGATE_OPEN ||
4208 element == EL_SWITCHGATE_OPENING)
4210 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4212 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4214 else if (element == EL_SWITCHGATE_CLOSED ||
4215 element == EL_SWITCHGATE_CLOSING)
4217 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4219 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4224 static int getInvisibleActiveFromInvisibleElement(int element)
4226 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4227 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4228 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4232 static int getInvisibleFromInvisibleActiveElement(int element)
4234 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4235 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4236 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4240 static void RedrawAllLightSwitchesAndInvisibleElements()
4244 SCAN_PLAYFIELD(x, y)
4246 int element = Feld[x][y];
4248 if (element == EL_LIGHT_SWITCH &&
4249 game.light_time_left > 0)
4251 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4252 DrawLevelField(x, y);
4254 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4255 game.light_time_left == 0)
4257 Feld[x][y] = EL_LIGHT_SWITCH;
4258 DrawLevelField(x, y);
4260 else if (element == EL_EMC_DRIPPER &&
4261 game.light_time_left > 0)
4263 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4264 DrawLevelField(x, y);
4266 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4267 game.light_time_left == 0)
4269 Feld[x][y] = EL_EMC_DRIPPER;
4270 DrawLevelField(x, y);
4272 else if (element == EL_INVISIBLE_STEELWALL ||
4273 element == EL_INVISIBLE_WALL ||
4274 element == EL_INVISIBLE_SAND)
4276 if (game.light_time_left > 0)
4277 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4279 DrawLevelField(x, y);
4281 /* uncrumble neighbour fields, if needed */
4282 if (element == EL_INVISIBLE_SAND)
4283 DrawLevelFieldCrumbledSandNeighbours(x, y);
4285 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4286 element == EL_INVISIBLE_WALL_ACTIVE ||
4287 element == EL_INVISIBLE_SAND_ACTIVE)
4289 if (game.light_time_left == 0)
4290 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4292 DrawLevelField(x, y);
4294 /* re-crumble neighbour fields, if needed */
4295 if (element == EL_INVISIBLE_SAND)
4296 DrawLevelFieldCrumbledSandNeighbours(x, y);
4301 static void RedrawAllInvisibleElementsForLenses()
4305 SCAN_PLAYFIELD(x, y)
4307 int element = Feld[x][y];
4309 if (element == EL_EMC_DRIPPER &&
4310 game.lenses_time_left > 0)
4312 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4313 DrawLevelField(x, y);
4315 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4316 game.lenses_time_left == 0)
4318 Feld[x][y] = EL_EMC_DRIPPER;
4319 DrawLevelField(x, y);
4321 else if (element == EL_INVISIBLE_STEELWALL ||
4322 element == EL_INVISIBLE_WALL ||
4323 element == EL_INVISIBLE_SAND)
4325 if (game.lenses_time_left > 0)
4326 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4328 DrawLevelField(x, y);
4330 /* uncrumble neighbour fields, if needed */
4331 if (element == EL_INVISIBLE_SAND)
4332 DrawLevelFieldCrumbledSandNeighbours(x, y);
4334 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4335 element == EL_INVISIBLE_WALL_ACTIVE ||
4336 element == EL_INVISIBLE_SAND_ACTIVE)
4338 if (game.lenses_time_left == 0)
4339 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4341 DrawLevelField(x, y);
4343 /* re-crumble neighbour fields, if needed */
4344 if (element == EL_INVISIBLE_SAND)
4345 DrawLevelFieldCrumbledSandNeighbours(x, y);
4350 static void RedrawAllInvisibleElementsForMagnifier()
4354 SCAN_PLAYFIELD(x, y)
4356 int element = Feld[x][y];
4358 if (element == EL_EMC_FAKE_GRASS &&
4359 game.magnify_time_left > 0)
4361 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4362 DrawLevelField(x, y);
4364 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4365 game.magnify_time_left == 0)
4367 Feld[x][y] = EL_EMC_FAKE_GRASS;
4368 DrawLevelField(x, y);
4370 else if (IS_GATE_GRAY(element) &&
4371 game.magnify_time_left > 0)
4373 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4374 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4375 IS_EM_GATE_GRAY(element) ?
4376 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4377 IS_EMC_GATE_GRAY(element) ?
4378 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4380 DrawLevelField(x, y);
4382 else if (IS_GATE_GRAY_ACTIVE(element) &&
4383 game.magnify_time_left == 0)
4385 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4386 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4387 IS_EM_GATE_GRAY_ACTIVE(element) ?
4388 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4389 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4390 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4392 DrawLevelField(x, y);
4397 static void ToggleLightSwitch(int x, int y)
4399 int element = Feld[x][y];
4401 game.light_time_left =
4402 (element == EL_LIGHT_SWITCH ?
4403 level.time_light * FRAMES_PER_SECOND : 0);
4405 RedrawAllLightSwitchesAndInvisibleElements();
4408 static void ActivateTimegateSwitch(int x, int y)
4412 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4414 SCAN_PLAYFIELD(xx, yy)
4416 int element = Feld[xx][yy];
4418 if (element == EL_TIMEGATE_CLOSED ||
4419 element == EL_TIMEGATE_CLOSING)
4421 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4422 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4426 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4428 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4429 DrawLevelField(xx, yy);
4435 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4438 void Impact(int x, int y)
4440 boolean last_line = (y == lev_fieldy - 1);
4441 boolean object_hit = FALSE;
4442 boolean impact = (last_line || object_hit);
4443 int element = Feld[x][y];
4444 int smashed = EL_STEELWALL;
4446 if (!last_line) /* check if element below was hit */
4448 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4451 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4452 MovDir[x][y + 1] != MV_DOWN ||
4453 MovPos[x][y + 1] <= TILEY / 2));
4455 /* do not smash moving elements that left the smashed field in time */
4456 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4457 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4460 #if USE_QUICKSAND_IMPACT_BUGFIX
4461 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4463 RemoveMovingField(x, y + 1);
4464 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4465 Feld[x][y + 2] = EL_ROCK;
4466 DrawLevelField(x, y + 2);
4473 smashed = MovingOrBlocked2Element(x, y + 1);
4475 impact = (last_line || object_hit);
4478 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4480 SplashAcid(x, y + 1);
4484 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4485 /* only reset graphic animation if graphic really changes after impact */
4487 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4489 ResetGfxAnimation(x, y);
4490 DrawLevelField(x, y);
4493 if (impact && CAN_EXPLODE_IMPACT(element))
4498 else if (impact && element == EL_PEARL)
4500 ResetGfxAnimation(x, y);
4502 Feld[x][y] = EL_PEARL_BREAKING;
4503 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4506 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4508 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4513 if (impact && element == EL_AMOEBA_DROP)
4515 if (object_hit && IS_PLAYER(x, y + 1))
4516 KillPlayerUnlessEnemyProtected(x, y + 1);
4517 else if (object_hit && smashed == EL_PENGUIN)
4521 Feld[x][y] = EL_AMOEBA_GROWING;
4522 Store[x][y] = EL_AMOEBA_WET;
4524 ResetRandomAnimationValue(x, y);
4529 if (object_hit) /* check which object was hit */
4531 if (CAN_PASS_MAGIC_WALL(element) &&
4532 (smashed == EL_MAGIC_WALL ||
4533 smashed == EL_BD_MAGIC_WALL))
4536 int activated_magic_wall =
4537 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4538 EL_BD_MAGIC_WALL_ACTIVE);
4540 /* activate magic wall / mill */
4541 SCAN_PLAYFIELD(xx, yy)
4542 if (Feld[xx][yy] == smashed)
4543 Feld[xx][yy] = activated_magic_wall;
4545 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4546 game.magic_wall_active = TRUE;
4548 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4549 SND_MAGIC_WALL_ACTIVATING :
4550 SND_BD_MAGIC_WALL_ACTIVATING));
4553 if (IS_PLAYER(x, y + 1))
4555 if (CAN_SMASH_PLAYER(element))
4557 KillPlayerUnlessEnemyProtected(x, y + 1);
4561 else if (smashed == EL_PENGUIN)
4563 if (CAN_SMASH_PLAYER(element))
4569 else if (element == EL_BD_DIAMOND)
4571 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4577 else if (((element == EL_SP_INFOTRON ||
4578 element == EL_SP_ZONK) &&
4579 (smashed == EL_SP_SNIKSNAK ||
4580 smashed == EL_SP_ELECTRON ||
4581 smashed == EL_SP_DISK_ORANGE)) ||
4582 (element == EL_SP_INFOTRON &&
4583 smashed == EL_SP_DISK_YELLOW))
4588 else if (CAN_SMASH_EVERYTHING(element))
4590 if (IS_CLASSIC_ENEMY(smashed) ||
4591 CAN_EXPLODE_SMASHED(smashed))
4596 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4598 if (smashed == EL_LAMP ||
4599 smashed == EL_LAMP_ACTIVE)
4604 else if (smashed == EL_NUT)
4606 Feld[x][y + 1] = EL_NUT_BREAKING;
4607 PlayLevelSound(x, y, SND_NUT_BREAKING);
4608 RaiseScoreElement(EL_NUT);
4611 else if (smashed == EL_PEARL)
4613 ResetGfxAnimation(x, y);
4615 Feld[x][y + 1] = EL_PEARL_BREAKING;
4616 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4619 else if (smashed == EL_DIAMOND)
4621 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4622 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4625 else if (IS_BELT_SWITCH(smashed))
4627 ToggleBeltSwitch(x, y + 1);
4629 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4630 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4632 ToggleSwitchgateSwitch(x, y + 1);
4634 else if (smashed == EL_LIGHT_SWITCH ||
4635 smashed == EL_LIGHT_SWITCH_ACTIVE)
4637 ToggleLightSwitch(x, y + 1);
4642 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4645 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4647 CheckElementChangeBySide(x, y + 1, smashed, element,
4648 CE_SWITCHED, CH_SIDE_TOP);
4649 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4655 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4660 /* play sound of magic wall / mill */
4662 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4663 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4665 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4666 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4667 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4668 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4673 /* play sound of object that hits the ground */
4674 if (last_line || object_hit)
4675 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4678 inline static void TurnRoundExt(int x, int y)
4690 { 0, 0 }, { 0, 0 }, { 0, 0 },
4695 int left, right, back;
4699 { MV_DOWN, MV_UP, MV_RIGHT },
4700 { MV_UP, MV_DOWN, MV_LEFT },
4702 { MV_LEFT, MV_RIGHT, MV_DOWN },
4706 { MV_RIGHT, MV_LEFT, MV_UP }
4709 int element = Feld[x][y];
4710 int move_pattern = element_info[element].move_pattern;
4712 int old_move_dir = MovDir[x][y];
4713 int left_dir = turn[old_move_dir].left;
4714 int right_dir = turn[old_move_dir].right;
4715 int back_dir = turn[old_move_dir].back;
4717 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4718 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4719 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4720 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4722 int left_x = x + left_dx, left_y = y + left_dy;
4723 int right_x = x + right_dx, right_y = y + right_dy;
4724 int move_x = x + move_dx, move_y = y + move_dy;
4728 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4730 TestIfBadThingTouchesOtherBadThing(x, y);
4732 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4733 MovDir[x][y] = right_dir;
4734 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4735 MovDir[x][y] = left_dir;
4737 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4739 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4742 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4744 TestIfBadThingTouchesOtherBadThing(x, y);
4746 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4747 MovDir[x][y] = left_dir;
4748 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4749 MovDir[x][y] = right_dir;
4751 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4753 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4756 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4758 TestIfBadThingTouchesOtherBadThing(x, y);
4760 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4761 MovDir[x][y] = left_dir;
4762 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4763 MovDir[x][y] = right_dir;
4765 if (MovDir[x][y] != old_move_dir)
4768 else if (element == EL_YAMYAM)
4770 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4771 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4773 if (can_turn_left && can_turn_right)
4774 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4775 else if (can_turn_left)
4776 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4777 else if (can_turn_right)
4778 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4780 MovDir[x][y] = back_dir;
4782 MovDelay[x][y] = 16 + 16 * RND(3);
4784 else if (element == EL_DARK_YAMYAM)
4786 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4788 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4791 if (can_turn_left && can_turn_right)
4792 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4793 else if (can_turn_left)
4794 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4795 else if (can_turn_right)
4796 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4798 MovDir[x][y] = back_dir;
4800 MovDelay[x][y] = 16 + 16 * RND(3);
4802 else if (element == EL_PACMAN)
4804 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4805 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4807 if (can_turn_left && can_turn_right)
4808 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4809 else if (can_turn_left)
4810 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4811 else if (can_turn_right)
4812 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4814 MovDir[x][y] = back_dir;
4816 MovDelay[x][y] = 6 + RND(40);
4818 else if (element == EL_PIG)
4820 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4821 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4822 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4823 boolean should_turn_left, should_turn_right, should_move_on;
4825 int rnd = RND(rnd_value);
4827 should_turn_left = (can_turn_left &&
4829 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4830 y + back_dy + left_dy)));
4831 should_turn_right = (can_turn_right &&
4833 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4834 y + back_dy + right_dy)));
4835 should_move_on = (can_move_on &&
4838 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4839 y + move_dy + left_dy) ||
4840 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4841 y + move_dy + right_dy)));
4843 if (should_turn_left || should_turn_right || should_move_on)
4845 if (should_turn_left && should_turn_right && should_move_on)
4846 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4847 rnd < 2 * rnd_value / 3 ? right_dir :
4849 else if (should_turn_left && should_turn_right)
4850 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4851 else if (should_turn_left && should_move_on)
4852 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4853 else if (should_turn_right && should_move_on)
4854 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4855 else if (should_turn_left)
4856 MovDir[x][y] = left_dir;
4857 else if (should_turn_right)
4858 MovDir[x][y] = right_dir;
4859 else if (should_move_on)
4860 MovDir[x][y] = old_move_dir;
4862 else if (can_move_on && rnd > rnd_value / 8)
4863 MovDir[x][y] = old_move_dir;
4864 else if (can_turn_left && can_turn_right)
4865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4866 else if (can_turn_left && rnd > rnd_value / 8)
4867 MovDir[x][y] = left_dir;
4868 else if (can_turn_right && rnd > rnd_value/8)
4869 MovDir[x][y] = right_dir;
4871 MovDir[x][y] = back_dir;
4873 xx = x + move_xy[MovDir[x][y]].dx;
4874 yy = y + move_xy[MovDir[x][y]].dy;
4876 if (!IN_LEV_FIELD(xx, yy) ||
4877 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4878 MovDir[x][y] = old_move_dir;
4882 else if (element == EL_DRAGON)
4884 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4885 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4886 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4888 int rnd = RND(rnd_value);
4890 if (can_move_on && rnd > rnd_value / 8)
4891 MovDir[x][y] = old_move_dir;
4892 else if (can_turn_left && can_turn_right)
4893 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4894 else if (can_turn_left && rnd > rnd_value / 8)
4895 MovDir[x][y] = left_dir;
4896 else if (can_turn_right && rnd > rnd_value / 8)
4897 MovDir[x][y] = right_dir;
4899 MovDir[x][y] = back_dir;
4901 xx = x + move_xy[MovDir[x][y]].dx;
4902 yy = y + move_xy[MovDir[x][y]].dy;
4904 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4905 MovDir[x][y] = old_move_dir;
4909 else if (element == EL_MOLE)
4911 boolean can_move_on =
4912 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4913 IS_AMOEBOID(Feld[move_x][move_y]) ||
4914 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4917 boolean can_turn_left =
4918 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4919 IS_AMOEBOID(Feld[left_x][left_y])));
4921 boolean can_turn_right =
4922 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4923 IS_AMOEBOID(Feld[right_x][right_y])));
4925 if (can_turn_left && can_turn_right)
4926 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4927 else if (can_turn_left)
4928 MovDir[x][y] = left_dir;
4930 MovDir[x][y] = right_dir;
4933 if (MovDir[x][y] != old_move_dir)
4936 else if (element == EL_BALLOON)
4938 MovDir[x][y] = game.wind_direction;
4941 else if (element == EL_SPRING)
4943 #if USE_NEW_SPRING_BUMPER
4944 if (MovDir[x][y] & MV_HORIZONTAL)
4946 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4947 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4949 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4950 ResetGfxAnimation(move_x, move_y);
4951 DrawLevelField(move_x, move_y);
4953 MovDir[x][y] = back_dir;
4955 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4956 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4957 MovDir[x][y] = MV_NONE;
4960 if (MovDir[x][y] & MV_HORIZONTAL &&
4961 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4962 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4963 MovDir[x][y] = MV_NONE;
4968 else if (element == EL_ROBOT ||
4969 element == EL_SATELLITE ||
4970 element == EL_PENGUIN ||
4971 element == EL_EMC_ANDROID)
4973 int attr_x = -1, attr_y = -1;
4984 for (i = 0; i < MAX_PLAYERS; i++)
4986 struct PlayerInfo *player = &stored_player[i];
4987 int jx = player->jx, jy = player->jy;
4989 if (!player->active)
4993 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5001 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5002 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5003 game.engine_version < VERSION_IDENT(3,1,0,0)))
5009 if (element == EL_PENGUIN)
5012 static int xy[4][2] =
5020 for (i = 0; i < NUM_DIRECTIONS; i++)
5022 int ex = x + xy[i][0];
5023 int ey = y + xy[i][1];
5025 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5034 MovDir[x][y] = MV_NONE;
5036 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5037 else if (attr_x > x)
5038 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5040 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5041 else if (attr_y > y)
5042 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5044 if (element == EL_ROBOT)
5048 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5049 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5050 Moving2Blocked(x, y, &newx, &newy);
5052 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5053 MovDelay[x][y] = 8 + 8 * !RND(3);
5055 MovDelay[x][y] = 16;
5057 else if (element == EL_PENGUIN)
5063 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5065 boolean first_horiz = RND(2);
5066 int new_move_dir = MovDir[x][y];
5069 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5070 Moving2Blocked(x, y, &newx, &newy);
5072 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5076 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5077 Moving2Blocked(x, y, &newx, &newy);
5079 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5082 MovDir[x][y] = old_move_dir;
5086 else if (element == EL_SATELLITE)
5092 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5094 boolean first_horiz = RND(2);
5095 int new_move_dir = MovDir[x][y];
5098 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5099 Moving2Blocked(x, y, &newx, &newy);
5101 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5105 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5106 Moving2Blocked(x, y, &newx, &newy);
5108 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5111 MovDir[x][y] = old_move_dir;
5115 else if (element == EL_EMC_ANDROID)
5117 static int check_pos[16] =
5119 -1, /* 0 => (invalid) */
5120 7, /* 1 => MV_LEFT */
5121 3, /* 2 => MV_RIGHT */
5122 -1, /* 3 => (invalid) */
5124 0, /* 5 => MV_LEFT | MV_UP */
5125 2, /* 6 => MV_RIGHT | MV_UP */
5126 -1, /* 7 => (invalid) */
5127 5, /* 8 => MV_DOWN */
5128 6, /* 9 => MV_LEFT | MV_DOWN */
5129 4, /* 10 => MV_RIGHT | MV_DOWN */
5130 -1, /* 11 => (invalid) */
5131 -1, /* 12 => (invalid) */
5132 -1, /* 13 => (invalid) */
5133 -1, /* 14 => (invalid) */
5134 -1, /* 15 => (invalid) */
5142 { -1, -1, MV_LEFT | MV_UP },
5144 { +1, -1, MV_RIGHT | MV_UP },
5145 { +1, 0, MV_RIGHT },
5146 { +1, +1, MV_RIGHT | MV_DOWN },
5148 { -1, +1, MV_LEFT | MV_DOWN },
5151 int start_pos, check_order;
5152 boolean can_clone = FALSE;
5155 /* check if there is any free field around current position */
5156 for (i = 0; i < 8; i++)
5158 int newx = x + check_xy[i].dx;
5159 int newy = y + check_xy[i].dy;
5161 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5169 if (can_clone) /* randomly find an element to clone */
5173 start_pos = check_pos[RND(8)];
5174 check_order = (RND(2) ? -1 : +1);
5176 for (i = 0; i < 8; i++)
5178 int pos_raw = start_pos + i * check_order;
5179 int pos = (pos_raw + 8) % 8;
5180 int newx = x + check_xy[pos].dx;
5181 int newy = y + check_xy[pos].dy;
5183 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5185 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5186 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5188 Store[x][y] = Feld[newx][newy];
5197 if (can_clone) /* randomly find a direction to move */
5201 start_pos = check_pos[RND(8)];
5202 check_order = (RND(2) ? -1 : +1);
5204 for (i = 0; i < 8; i++)
5206 int pos_raw = start_pos + i * check_order;
5207 int pos = (pos_raw + 8) % 8;
5208 int newx = x + check_xy[pos].dx;
5209 int newy = y + check_xy[pos].dy;
5210 int new_move_dir = check_xy[pos].dir;
5212 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5214 MovDir[x][y] = new_move_dir;
5215 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5224 if (can_clone) /* cloning and moving successful */
5227 /* cannot clone -- try to move towards player */
5229 start_pos = check_pos[MovDir[x][y] & 0x0f];
5230 check_order = (RND(2) ? -1 : +1);
5232 for (i = 0; i < 3; i++)
5234 /* first check start_pos, then previous/next or (next/previous) pos */
5235 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5236 int pos = (pos_raw + 8) % 8;
5237 int newx = x + check_xy[pos].dx;
5238 int newy = y + check_xy[pos].dy;
5239 int new_move_dir = check_xy[pos].dir;
5241 if (IS_PLAYER(newx, newy))
5244 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5246 MovDir[x][y] = new_move_dir;
5247 MovDelay[x][y] = level.android_move_time * 8 + 1;
5254 else if (move_pattern == MV_TURNING_LEFT ||
5255 move_pattern == MV_TURNING_RIGHT ||
5256 move_pattern == MV_TURNING_LEFT_RIGHT ||
5257 move_pattern == MV_TURNING_RIGHT_LEFT ||
5258 move_pattern == MV_TURNING_RANDOM ||
5259 move_pattern == MV_ALL_DIRECTIONS)
5261 boolean can_turn_left =
5262 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5263 boolean can_turn_right =
5264 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5266 if (element_info[element].move_stepsize == 0) /* "not moving" */
5269 if (move_pattern == MV_TURNING_LEFT)
5270 MovDir[x][y] = left_dir;
5271 else if (move_pattern == MV_TURNING_RIGHT)
5272 MovDir[x][y] = right_dir;
5273 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5274 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5275 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5276 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5277 else if (move_pattern == MV_TURNING_RANDOM)
5278 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5279 can_turn_right && !can_turn_left ? right_dir :
5280 RND(2) ? left_dir : right_dir);
5281 else if (can_turn_left && can_turn_right)
5282 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5283 else if (can_turn_left)
5284 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5285 else if (can_turn_right)
5286 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5288 MovDir[x][y] = back_dir;
5290 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5292 else if (move_pattern == MV_HORIZONTAL ||
5293 move_pattern == MV_VERTICAL)
5295 if (move_pattern & old_move_dir)
5296 MovDir[x][y] = back_dir;
5297 else if (move_pattern == MV_HORIZONTAL)
5298 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5299 else if (move_pattern == MV_VERTICAL)
5300 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5302 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5304 else if (move_pattern & MV_ANY_DIRECTION)
5306 MovDir[x][y] = move_pattern;
5307 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5309 else if (move_pattern & MV_WIND_DIRECTION)
5311 MovDir[x][y] = game.wind_direction;
5312 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5314 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5316 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5317 MovDir[x][y] = left_dir;
5318 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5319 MovDir[x][y] = right_dir;
5321 if (MovDir[x][y] != old_move_dir)
5322 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5324 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5326 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5327 MovDir[x][y] = right_dir;
5328 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5329 MovDir[x][y] = left_dir;
5331 if (MovDir[x][y] != old_move_dir)
5332 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5334 else if (move_pattern == MV_TOWARDS_PLAYER ||
5335 move_pattern == MV_AWAY_FROM_PLAYER)
5337 int attr_x = -1, attr_y = -1;
5339 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5350 for (i = 0; i < MAX_PLAYERS; i++)
5352 struct PlayerInfo *player = &stored_player[i];
5353 int jx = player->jx, jy = player->jy;
5355 if (!player->active)
5359 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5367 MovDir[x][y] = MV_NONE;
5369 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5370 else if (attr_x > x)
5371 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5373 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5374 else if (attr_y > y)
5375 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5377 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5379 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5381 boolean first_horiz = RND(2);
5382 int new_move_dir = MovDir[x][y];
5384 if (element_info[element].move_stepsize == 0) /* "not moving" */
5386 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5387 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5393 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5394 Moving2Blocked(x, y, &newx, &newy);
5396 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5400 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5401 Moving2Blocked(x, y, &newx, &newy);
5403 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5406 MovDir[x][y] = old_move_dir;
5409 else if (move_pattern == MV_WHEN_PUSHED ||
5410 move_pattern == MV_WHEN_DROPPED)
5412 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5413 MovDir[x][y] = MV_NONE;
5417 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5419 static int test_xy[7][2] =
5429 static int test_dir[7] =
5439 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5440 int move_preference = -1000000; /* start with very low preference */
5441 int new_move_dir = MV_NONE;
5442 int start_test = RND(4);
5445 for (i = 0; i < NUM_DIRECTIONS; i++)
5447 int move_dir = test_dir[start_test + i];
5448 int move_dir_preference;
5450 xx = x + test_xy[start_test + i][0];
5451 yy = y + test_xy[start_test + i][1];
5453 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5454 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5456 new_move_dir = move_dir;
5461 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5464 move_dir_preference = -1 * RunnerVisit[xx][yy];
5465 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5466 move_dir_preference = PlayerVisit[xx][yy];
5468 if (move_dir_preference > move_preference)
5470 /* prefer field that has not been visited for the longest time */
5471 move_preference = move_dir_preference;
5472 new_move_dir = move_dir;
5474 else if (move_dir_preference == move_preference &&
5475 move_dir == old_move_dir)
5477 /* prefer last direction when all directions are preferred equally */
5478 move_preference = move_dir_preference;
5479 new_move_dir = move_dir;
5483 MovDir[x][y] = new_move_dir;
5484 if (old_move_dir != new_move_dir)
5485 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5489 static void TurnRound(int x, int y)
5491 int direction = MovDir[x][y];
5495 GfxDir[x][y] = MovDir[x][y];
5497 if (direction != MovDir[x][y])
5501 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5503 ResetGfxFrame(x, y, FALSE);
5506 static boolean JustBeingPushed(int x, int y)
5510 for (i = 0; i < MAX_PLAYERS; i++)
5512 struct PlayerInfo *player = &stored_player[i];
5514 if (player->active && player->is_pushing && player->MovPos)
5516 int next_jx = player->jx + (player->jx - player->last_jx);
5517 int next_jy = player->jy + (player->jy - player->last_jy);
5519 if (x == next_jx && y == next_jy)
5527 void StartMoving(int x, int y)
5529 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5530 int element = Feld[x][y];
5535 if (MovDelay[x][y] == 0)
5536 GfxAction[x][y] = ACTION_DEFAULT;
5538 if (CAN_FALL(element) && y < lev_fieldy - 1)
5540 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5541 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5542 if (JustBeingPushed(x, y))
5545 if (element == EL_QUICKSAND_FULL)
5547 if (IS_FREE(x, y + 1))
5549 InitMovingField(x, y, MV_DOWN);
5550 started_moving = TRUE;
5552 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5553 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5554 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5555 Store[x][y] = EL_ROCK;
5557 Store[x][y] = EL_ROCK;
5560 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5562 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5564 if (!MovDelay[x][y])
5565 MovDelay[x][y] = TILEY + 1;
5574 Feld[x][y] = EL_QUICKSAND_EMPTY;
5575 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5576 Store[x][y + 1] = Store[x][y];
5579 PlayLevelSoundAction(x, y, ACTION_FILLING);
5582 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5583 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5585 InitMovingField(x, y, MV_DOWN);
5586 started_moving = TRUE;
5588 Feld[x][y] = EL_QUICKSAND_FILLING;
5589 Store[x][y] = element;
5591 PlayLevelSoundAction(x, y, ACTION_FILLING);
5593 else if (element == EL_MAGIC_WALL_FULL)
5595 if (IS_FREE(x, y + 1))
5597 InitMovingField(x, y, MV_DOWN);
5598 started_moving = TRUE;
5600 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5601 Store[x][y] = EL_CHANGED(Store[x][y]);
5603 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5605 if (!MovDelay[x][y])
5606 MovDelay[x][y] = TILEY/4 + 1;
5615 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5616 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5617 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5621 else if (element == EL_BD_MAGIC_WALL_FULL)
5623 if (IS_FREE(x, y + 1))
5625 InitMovingField(x, y, MV_DOWN);
5626 started_moving = TRUE;
5628 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5629 Store[x][y] = EL_CHANGED2(Store[x][y]);
5631 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5633 if (!MovDelay[x][y])
5634 MovDelay[x][y] = TILEY/4 + 1;
5643 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5644 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5645 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5649 else if (CAN_PASS_MAGIC_WALL(element) &&
5650 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5651 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5653 InitMovingField(x, y, MV_DOWN);
5654 started_moving = TRUE;
5657 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5658 EL_BD_MAGIC_WALL_FILLING);
5659 Store[x][y] = element;
5661 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5663 SplashAcid(x, y + 1);
5665 InitMovingField(x, y, MV_DOWN);
5666 started_moving = TRUE;
5668 Store[x][y] = EL_ACID;
5671 #if USE_FIX_IMPACT_COLLISION
5672 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5673 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5675 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5676 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5678 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5679 CAN_FALL(element) && WasJustFalling[x][y] &&
5680 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5682 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5683 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5684 (Feld[x][y + 1] == EL_BLOCKED)))
5686 /* this is needed for a special case not covered by calling "Impact()"
5687 from "ContinueMoving()": if an element moves to a tile directly below
5688 another element which was just falling on that tile (which was empty
5689 in the previous frame), the falling element above would just stop
5690 instead of smashing the element below (in previous version, the above
5691 element was just checked for "moving" instead of "falling", resulting
5692 in incorrect smashes caused by horizontal movement of the above
5693 element; also, the case of the player being the element to smash was
5694 simply not covered here... :-/ ) */
5696 CheckCollision[x][y] = 0;
5697 CheckImpact[x][y] = 0;
5701 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5703 if (MovDir[x][y] == MV_NONE)
5705 InitMovingField(x, y, MV_DOWN);
5706 started_moving = TRUE;
5709 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5711 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5712 MovDir[x][y] = MV_DOWN;
5714 InitMovingField(x, y, MV_DOWN);
5715 started_moving = TRUE;
5717 else if (element == EL_AMOEBA_DROP)
5719 Feld[x][y] = EL_AMOEBA_GROWING;
5720 Store[x][y] = EL_AMOEBA_WET;
5722 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5723 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5724 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5725 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5727 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5728 (IS_FREE(x - 1, y + 1) ||
5729 Feld[x - 1][y + 1] == EL_ACID));
5730 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5731 (IS_FREE(x + 1, y + 1) ||
5732 Feld[x + 1][y + 1] == EL_ACID));
5733 boolean can_fall_any = (can_fall_left || can_fall_right);
5734 boolean can_fall_both = (can_fall_left && can_fall_right);
5735 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5737 #if USE_NEW_ALL_SLIPPERY
5738 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5740 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5741 can_fall_right = FALSE;
5742 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5743 can_fall_left = FALSE;
5744 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5745 can_fall_right = FALSE;
5746 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5747 can_fall_left = FALSE;
5749 can_fall_any = (can_fall_left || can_fall_right);
5750 can_fall_both = FALSE;
5753 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5755 if (slippery_type == SLIPPERY_ONLY_LEFT)
5756 can_fall_right = FALSE;
5757 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5758 can_fall_left = FALSE;
5759 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5760 can_fall_right = FALSE;
5761 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5762 can_fall_left = FALSE;
5764 can_fall_any = (can_fall_left || can_fall_right);
5765 can_fall_both = (can_fall_left && can_fall_right);
5769 #if USE_NEW_ALL_SLIPPERY
5771 #if USE_NEW_SP_SLIPPERY
5772 /* !!! better use the same properties as for custom elements here !!! */
5773 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5774 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5776 can_fall_right = FALSE; /* slip down on left side */
5777 can_fall_both = FALSE;
5782 #if USE_NEW_ALL_SLIPPERY
5785 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5786 can_fall_right = FALSE; /* slip down on left side */
5788 can_fall_left = !(can_fall_right = RND(2));
5790 can_fall_both = FALSE;
5795 if (game.emulation == EMU_BOULDERDASH ||
5796 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5797 can_fall_right = FALSE; /* slip down on left side */
5799 can_fall_left = !(can_fall_right = RND(2));
5801 can_fall_both = FALSE;
5807 /* if not determined otherwise, prefer left side for slipping down */
5808 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5809 started_moving = TRUE;
5813 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5815 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5818 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5819 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5820 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5821 int belt_dir = game.belt_dir[belt_nr];
5823 if ((belt_dir == MV_LEFT && left_is_free) ||
5824 (belt_dir == MV_RIGHT && right_is_free))
5826 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5828 InitMovingField(x, y, belt_dir);
5829 started_moving = TRUE;
5831 Pushed[x][y] = TRUE;
5832 Pushed[nextx][y] = TRUE;
5834 GfxAction[x][y] = ACTION_DEFAULT;
5838 MovDir[x][y] = 0; /* if element was moving, stop it */
5843 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5845 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5847 if (CAN_MOVE(element) && !started_moving)
5850 int move_pattern = element_info[element].move_pattern;
5855 if (MovDir[x][y] == MV_NONE)
5857 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5858 x, y, element, element_info[element].token_name);
5859 printf("StartMoving(): This should never happen!\n");
5864 Moving2Blocked(x, y, &newx, &newy);
5866 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5869 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5870 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5872 WasJustMoving[x][y] = 0;
5873 CheckCollision[x][y] = 0;
5875 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5877 if (Feld[x][y] != element) /* element has changed */
5881 if (!MovDelay[x][y]) /* start new movement phase */
5883 /* all objects that can change their move direction after each step
5884 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5886 if (element != EL_YAMYAM &&
5887 element != EL_DARK_YAMYAM &&
5888 element != EL_PACMAN &&
5889 !(move_pattern & MV_ANY_DIRECTION) &&
5890 move_pattern != MV_TURNING_LEFT &&
5891 move_pattern != MV_TURNING_RIGHT &&
5892 move_pattern != MV_TURNING_LEFT_RIGHT &&
5893 move_pattern != MV_TURNING_RIGHT_LEFT &&
5894 move_pattern != MV_TURNING_RANDOM)
5898 if (MovDelay[x][y] && (element == EL_BUG ||
5899 element == EL_SPACESHIP ||
5900 element == EL_SP_SNIKSNAK ||
5901 element == EL_SP_ELECTRON ||
5902 element == EL_MOLE))
5903 DrawLevelField(x, y);
5907 if (MovDelay[x][y]) /* wait some time before next movement */
5911 if (element == EL_ROBOT ||
5912 element == EL_YAMYAM ||
5913 element == EL_DARK_YAMYAM)
5915 DrawLevelElementAnimationIfNeeded(x, y, element);
5916 PlayLevelSoundAction(x, y, ACTION_WAITING);
5918 else if (element == EL_SP_ELECTRON)
5919 DrawLevelElementAnimationIfNeeded(x, y, element);
5920 else if (element == EL_DRAGON)
5923 int dir = MovDir[x][y];
5924 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5925 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5926 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5927 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5928 dir == MV_UP ? IMG_FLAMES_1_UP :
5929 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5930 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5932 GfxAction[x][y] = ACTION_ATTACKING;
5934 if (IS_PLAYER(x, y))
5935 DrawPlayerField(x, y);
5937 DrawLevelField(x, y);
5939 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5941 for (i = 1; i <= 3; i++)
5943 int xx = x + i * dx;
5944 int yy = y + i * dy;
5945 int sx = SCREENX(xx);
5946 int sy = SCREENY(yy);
5947 int flame_graphic = graphic + (i - 1);
5949 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5954 int flamed = MovingOrBlocked2Element(xx, yy);
5958 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5960 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5961 RemoveMovingField(xx, yy);
5963 RemoveField(xx, yy);
5965 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5968 RemoveMovingField(xx, yy);
5971 ChangeDelay[xx][yy] = 0;
5973 Feld[xx][yy] = EL_FLAMES;
5975 if (IN_SCR_FIELD(sx, sy))
5977 DrawLevelFieldCrumbledSand(xx, yy);
5978 DrawGraphic(sx, sy, flame_graphic, frame);
5983 if (Feld[xx][yy] == EL_FLAMES)
5984 Feld[xx][yy] = EL_EMPTY;
5985 DrawLevelField(xx, yy);
5990 if (MovDelay[x][y]) /* element still has to wait some time */
5992 PlayLevelSoundAction(x, y, ACTION_WAITING);
5998 /* now make next step */
6000 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6002 if (DONT_COLLIDE_WITH(element) &&
6003 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6004 !PLAYER_ENEMY_PROTECTED(newx, newy))
6006 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6011 else if (CAN_MOVE_INTO_ACID(element) &&
6012 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6013 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6014 (MovDir[x][y] == MV_DOWN ||
6015 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6017 SplashAcid(newx, newy);
6018 Store[x][y] = EL_ACID;
6020 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6022 if (Feld[newx][newy] == EL_EXIT_OPEN)
6025 DrawLevelField(x, y);
6027 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6028 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6029 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6031 local_player->friends_still_needed--;
6032 if (!local_player->friends_still_needed &&
6033 !local_player->GameOver && AllPlayersGone)
6034 PlayerWins(local_player);
6038 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6040 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6041 DrawLevelField(newx, newy);
6043 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6045 else if (!IS_FREE(newx, newy))
6047 GfxAction[x][y] = ACTION_WAITING;
6049 if (IS_PLAYER(x, y))
6050 DrawPlayerField(x, y);
6052 DrawLevelField(x, y);
6057 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6059 if (IS_FOOD_PIG(Feld[newx][newy]))
6061 if (IS_MOVING(newx, newy))
6062 RemoveMovingField(newx, newy);
6065 Feld[newx][newy] = EL_EMPTY;
6066 DrawLevelField(newx, newy);
6069 PlayLevelSound(x, y, SND_PIG_DIGGING);
6071 else if (!IS_FREE(newx, newy))
6073 if (IS_PLAYER(x, y))
6074 DrawPlayerField(x, y);
6076 DrawLevelField(x, y);
6081 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6083 if (Store[x][y] != EL_EMPTY)
6085 boolean can_clone = FALSE;
6088 /* check if element to clone is still there */
6089 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6091 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6099 /* cannot clone or target field not free anymore -- do not clone */
6100 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6101 Store[x][y] = EL_EMPTY;
6104 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6106 if (IS_MV_DIAGONAL(MovDir[x][y]))
6108 int diagonal_move_dir = MovDir[x][y];
6109 int stored = Store[x][y];
6110 int change_delay = 8;
6113 /* android is moving diagonally */
6115 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6117 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6118 GfxElement[x][y] = EL_EMC_ANDROID;
6119 GfxAction[x][y] = ACTION_SHRINKING;
6120 GfxDir[x][y] = diagonal_move_dir;
6121 ChangeDelay[x][y] = change_delay;
6123 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6126 DrawLevelGraphicAnimation(x, y, graphic);
6127 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6129 if (Feld[newx][newy] == EL_ACID)
6131 SplashAcid(newx, newy);
6136 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6138 Store[newx][newy] = EL_EMC_ANDROID;
6139 GfxElement[newx][newy] = EL_EMC_ANDROID;
6140 GfxAction[newx][newy] = ACTION_GROWING;
6141 GfxDir[newx][newy] = diagonal_move_dir;
6142 ChangeDelay[newx][newy] = change_delay;
6144 graphic = el_act_dir2img(GfxElement[newx][newy],
6145 GfxAction[newx][newy], GfxDir[newx][newy]);
6147 DrawLevelGraphicAnimation(newx, newy, graphic);
6148 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6154 Feld[newx][newy] = EL_EMPTY;
6155 DrawLevelField(newx, newy);
6157 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6160 else if (!IS_FREE(newx, newy))
6163 if (IS_PLAYER(x, y))
6164 DrawPlayerField(x, y);
6166 DrawLevelField(x, y);
6172 else if (IS_CUSTOM_ELEMENT(element) &&
6173 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6175 int new_element = Feld[newx][newy];
6177 if (!IS_FREE(newx, newy))
6179 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6180 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6183 /* no element can dig solid indestructible elements */
6184 if (IS_INDESTRUCTIBLE(new_element) &&
6185 !IS_DIGGABLE(new_element) &&
6186 !IS_COLLECTIBLE(new_element))
6189 if (AmoebaNr[newx][newy] &&
6190 (new_element == EL_AMOEBA_FULL ||
6191 new_element == EL_BD_AMOEBA ||
6192 new_element == EL_AMOEBA_GROWING))
6194 AmoebaCnt[AmoebaNr[newx][newy]]--;
6195 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6198 if (IS_MOVING(newx, newy))
6199 RemoveMovingField(newx, newy);
6202 RemoveField(newx, newy);
6203 DrawLevelField(newx, newy);
6206 /* if digged element was about to explode, prevent the explosion */
6207 ExplodeField[newx][newy] = EX_TYPE_NONE;
6209 PlayLevelSoundAction(x, y, action);
6212 Store[newx][newy] = EL_EMPTY;
6214 /* this makes it possible to leave the removed element again */
6215 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6216 Store[newx][newy] = new_element;
6218 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6220 int move_leave_element = element_info[element].move_leave_element;
6222 /* this makes it possible to leave the removed element again */
6223 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6224 new_element : move_leave_element);
6228 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6230 RunnerVisit[x][y] = FrameCounter;
6231 PlayerVisit[x][y] /= 8; /* expire player visit path */
6234 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6236 if (!IS_FREE(newx, newy))
6238 if (IS_PLAYER(x, y))
6239 DrawPlayerField(x, y);
6241 DrawLevelField(x, y);
6247 boolean wanna_flame = !RND(10);
6248 int dx = newx - x, dy = newy - y;
6249 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6250 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6251 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6252 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6253 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6254 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6257 IS_CLASSIC_ENEMY(element1) ||
6258 IS_CLASSIC_ENEMY(element2)) &&
6259 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6260 element1 != EL_FLAMES && element2 != EL_FLAMES)
6262 ResetGfxAnimation(x, y);
6263 GfxAction[x][y] = ACTION_ATTACKING;
6265 if (IS_PLAYER(x, y))
6266 DrawPlayerField(x, y);
6268 DrawLevelField(x, y);
6270 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6272 MovDelay[x][y] = 50;
6276 RemoveField(newx, newy);
6278 Feld[newx][newy] = EL_FLAMES;
6279 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6282 RemoveField(newx1, newy1);
6284 Feld[newx1][newy1] = EL_FLAMES;
6286 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6289 RemoveField(newx2, newy2);
6291 Feld[newx2][newy2] = EL_FLAMES;
6298 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6299 Feld[newx][newy] == EL_DIAMOND)
6301 if (IS_MOVING(newx, newy))
6302 RemoveMovingField(newx, newy);
6305 Feld[newx][newy] = EL_EMPTY;
6306 DrawLevelField(newx, newy);
6309 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6311 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6312 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6314 if (AmoebaNr[newx][newy])
6316 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6317 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6318 Feld[newx][newy] == EL_BD_AMOEBA)
6319 AmoebaCnt[AmoebaNr[newx][newy]]--;
6324 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6326 RemoveMovingField(newx, newy);
6329 if (IS_MOVING(newx, newy))
6331 RemoveMovingField(newx, newy);
6336 Feld[newx][newy] = EL_EMPTY;
6337 DrawLevelField(newx, newy);
6340 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6342 else if ((element == EL_PACMAN || element == EL_MOLE)
6343 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6345 if (AmoebaNr[newx][newy])
6347 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6348 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6349 Feld[newx][newy] == EL_BD_AMOEBA)
6350 AmoebaCnt[AmoebaNr[newx][newy]]--;
6353 if (element == EL_MOLE)
6355 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6356 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6358 ResetGfxAnimation(x, y);
6359 GfxAction[x][y] = ACTION_DIGGING;
6360 DrawLevelField(x, y);
6362 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6364 return; /* wait for shrinking amoeba */
6366 else /* element == EL_PACMAN */
6368 Feld[newx][newy] = EL_EMPTY;
6369 DrawLevelField(newx, newy);
6370 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6373 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6374 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6375 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6377 /* wait for shrinking amoeba to completely disappear */
6380 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6382 /* object was running against a wall */
6387 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6388 if (move_pattern & MV_ANY_DIRECTION &&
6389 move_pattern == MovDir[x][y])
6391 int blocking_element =
6392 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6394 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6397 element = Feld[x][y]; /* element might have changed */
6401 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6402 DrawLevelElementAnimation(x, y, element);
6404 if (DONT_TOUCH(element))
6405 TestIfBadThingTouchesPlayer(x, y);
6410 InitMovingField(x, y, MovDir[x][y]);
6412 PlayLevelSoundAction(x, y, ACTION_MOVING);
6416 ContinueMoving(x, y);
6419 void ContinueMoving(int x, int y)
6421 int element = Feld[x][y];
6422 struct ElementInfo *ei = &element_info[element];
6423 int direction = MovDir[x][y];
6424 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6425 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6426 int newx = x + dx, newy = y + dy;
6427 int stored = Store[x][y];
6428 int stored_new = Store[newx][newy];
6429 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6430 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6431 boolean last_line = (newy == lev_fieldy - 1);
6433 MovPos[x][y] += getElementMoveStepsize(x, y);
6435 if (pushed_by_player) /* special case: moving object pushed by player */
6436 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6438 if (ABS(MovPos[x][y]) < TILEX)
6440 DrawLevelField(x, y);
6442 return; /* element is still moving */
6445 /* element reached destination field */
6447 Feld[x][y] = EL_EMPTY;
6448 Feld[newx][newy] = element;
6449 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6451 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6453 element = Feld[newx][newy] = EL_ACID;
6455 else if (element == EL_MOLE)
6457 Feld[x][y] = EL_SAND;
6459 DrawLevelFieldCrumbledSandNeighbours(x, y);
6461 else if (element == EL_QUICKSAND_FILLING)
6463 element = Feld[newx][newy] = get_next_element(element);
6464 Store[newx][newy] = Store[x][y];
6466 else if (element == EL_QUICKSAND_EMPTYING)
6468 Feld[x][y] = get_next_element(element);
6469 element = Feld[newx][newy] = Store[x][y];
6471 else if (element == EL_MAGIC_WALL_FILLING)
6473 element = Feld[newx][newy] = get_next_element(element);
6474 if (!game.magic_wall_active)
6475 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6476 Store[newx][newy] = Store[x][y];
6478 else if (element == EL_MAGIC_WALL_EMPTYING)
6480 Feld[x][y] = get_next_element(element);
6481 if (!game.magic_wall_active)
6482 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6483 element = Feld[newx][newy] = Store[x][y];
6485 #if USE_NEW_CUSTOM_VALUE
6486 InitField(newx, newy, FALSE);
6489 else if (element == EL_BD_MAGIC_WALL_FILLING)
6491 element = Feld[newx][newy] = get_next_element(element);
6492 if (!game.magic_wall_active)
6493 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6494 Store[newx][newy] = Store[x][y];
6496 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6498 Feld[x][y] = get_next_element(element);
6499 if (!game.magic_wall_active)
6500 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6501 element = Feld[newx][newy] = Store[x][y];
6503 #if USE_NEW_CUSTOM_VALUE
6504 InitField(newx, newy, FALSE);
6507 else if (element == EL_AMOEBA_DROPPING)
6509 Feld[x][y] = get_next_element(element);
6510 element = Feld[newx][newy] = Store[x][y];
6512 else if (element == EL_SOKOBAN_OBJECT)
6515 Feld[x][y] = Back[x][y];
6517 if (Back[newx][newy])
6518 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6520 Back[x][y] = Back[newx][newy] = 0;
6523 Store[x][y] = EL_EMPTY;
6528 MovDelay[newx][newy] = 0;
6530 if (CAN_CHANGE_OR_HAS_ACTION(element))
6532 /* copy element change control values to new field */
6533 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6534 ChangePage[newx][newy] = ChangePage[x][y];
6535 ChangeCount[newx][newy] = ChangeCount[x][y];
6536 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6539 #if USE_NEW_CUSTOM_VALUE
6540 CustomValue[newx][newy] = CustomValue[x][y];
6543 ChangeDelay[x][y] = 0;
6544 ChangePage[x][y] = -1;
6545 ChangeCount[x][y] = 0;
6546 ChangeEvent[x][y] = -1;
6548 #if USE_NEW_CUSTOM_VALUE
6549 CustomValue[x][y] = 0;
6552 /* copy animation control values to new field */
6553 GfxFrame[newx][newy] = GfxFrame[x][y];
6554 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6555 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6556 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6558 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6560 /* some elements can leave other elements behind after moving */
6562 if (ei->move_leave_element != EL_EMPTY &&
6563 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6564 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6566 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6567 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6568 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6571 int move_leave_element = ei->move_leave_element;
6575 /* this makes it possible to leave the removed element again */
6576 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6577 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6579 /* this makes it possible to leave the removed element again */
6580 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6581 move_leave_element = stored;
6584 /* this makes it possible to leave the removed element again */
6585 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6586 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6587 move_leave_element = stored;
6590 Feld[x][y] = move_leave_element;
6592 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6593 MovDir[x][y] = direction;
6595 InitField(x, y, FALSE);
6597 if (GFX_CRUMBLED(Feld[x][y]))
6598 DrawLevelFieldCrumbledSandNeighbours(x, y);
6600 if (ELEM_IS_PLAYER(move_leave_element))
6601 RelocatePlayer(x, y, move_leave_element);
6604 /* do this after checking for left-behind element */
6605 ResetGfxAnimation(x, y); /* reset animation values for old field */
6607 if (!CAN_MOVE(element) ||
6608 (CAN_FALL(element) && direction == MV_DOWN &&
6609 (element == EL_SPRING ||
6610 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6611 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6612 GfxDir[x][y] = MovDir[newx][newy] = 0;
6614 DrawLevelField(x, y);
6615 DrawLevelField(newx, newy);
6617 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6619 /* prevent pushed element from moving on in pushed direction */
6620 if (pushed_by_player && CAN_MOVE(element) &&
6621 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6622 !(element_info[element].move_pattern & direction))
6623 TurnRound(newx, newy);
6625 /* prevent elements on conveyor belt from moving on in last direction */
6626 if (pushed_by_conveyor && CAN_FALL(element) &&
6627 direction & MV_HORIZONTAL)
6628 MovDir[newx][newy] = 0;
6630 if (!pushed_by_player)
6632 int nextx = newx + dx, nexty = newy + dy;
6633 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6635 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6637 if (CAN_FALL(element) && direction == MV_DOWN)
6638 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6640 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6641 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6643 #if USE_FIX_IMPACT_COLLISION
6644 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6645 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6649 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6651 TestIfBadThingTouchesPlayer(newx, newy);
6652 TestIfBadThingTouchesFriend(newx, newy);
6654 if (!IS_CUSTOM_ELEMENT(element))
6655 TestIfBadThingTouchesOtherBadThing(newx, newy);
6657 else if (element == EL_PENGUIN)
6658 TestIfFriendTouchesBadThing(newx, newy);
6660 /* give the player one last chance (one more frame) to move away */
6661 if (CAN_FALL(element) && direction == MV_DOWN &&
6662 (last_line || (!IS_FREE(x, newy + 1) &&
6663 (!IS_PLAYER(x, newy + 1) ||
6664 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6667 if (pushed_by_player && !game.use_change_when_pushing_bug)
6669 int push_side = MV_DIR_OPPOSITE(direction);
6670 struct PlayerInfo *player = PLAYERINFO(x, y);
6672 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6673 player->index_bit, push_side);
6674 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6675 player->index_bit, push_side);
6678 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6679 MovDelay[newx][newy] = 1;
6681 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6683 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6686 if (ChangePage[newx][newy] != -1) /* delayed change */
6688 int page = ChangePage[newx][newy];
6689 struct ElementChangeInfo *change = &ei->change_page[page];
6691 ChangePage[newx][newy] = -1;
6693 if (change->can_change)
6695 if (ChangeElement(newx, newy, element, page))
6697 if (change->post_change_function)
6698 change->post_change_function(newx, newy);
6702 if (change->has_action)
6703 ExecuteCustomElementAction(newx, newy, element, page);
6707 TestIfElementHitsCustomElement(newx, newy, direction);
6708 TestIfPlayerTouchesCustomElement(newx, newy);
6709 TestIfElementTouchesCustomElement(newx, newy);
6711 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6712 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6713 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6714 MV_DIR_OPPOSITE(direction));
6717 int AmoebeNachbarNr(int ax, int ay)
6720 int element = Feld[ax][ay];
6722 static int xy[4][2] =
6730 for (i = 0; i < NUM_DIRECTIONS; i++)
6732 int x = ax + xy[i][0];
6733 int y = ay + xy[i][1];
6735 if (!IN_LEV_FIELD(x, y))
6738 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6739 group_nr = AmoebaNr[x][y];
6745 void AmoebenVereinigen(int ax, int ay)
6747 int i, x, y, xx, yy;
6748 int new_group_nr = AmoebaNr[ax][ay];
6749 static int xy[4][2] =
6757 if (new_group_nr == 0)
6760 for (i = 0; i < NUM_DIRECTIONS; i++)
6765 if (!IN_LEV_FIELD(x, y))
6768 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6769 Feld[x][y] == EL_BD_AMOEBA ||
6770 Feld[x][y] == EL_AMOEBA_DEAD) &&
6771 AmoebaNr[x][y] != new_group_nr)
6773 int old_group_nr = AmoebaNr[x][y];
6775 if (old_group_nr == 0)
6778 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6779 AmoebaCnt[old_group_nr] = 0;
6780 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6781 AmoebaCnt2[old_group_nr] = 0;
6783 SCAN_PLAYFIELD(xx, yy)
6785 if (AmoebaNr[xx][yy] == old_group_nr)
6786 AmoebaNr[xx][yy] = new_group_nr;
6792 void AmoebeUmwandeln(int ax, int ay)
6796 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6798 int group_nr = AmoebaNr[ax][ay];
6803 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6804 printf("AmoebeUmwandeln(): This should never happen!\n");
6809 SCAN_PLAYFIELD(x, y)
6811 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6814 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6818 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6819 SND_AMOEBA_TURNING_TO_GEM :
6820 SND_AMOEBA_TURNING_TO_ROCK));
6825 static int xy[4][2] =
6833 for (i = 0; i < NUM_DIRECTIONS; i++)
6838 if (!IN_LEV_FIELD(x, y))
6841 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6843 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6844 SND_AMOEBA_TURNING_TO_GEM :
6845 SND_AMOEBA_TURNING_TO_ROCK));
6852 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6855 int group_nr = AmoebaNr[ax][ay];
6856 boolean done = FALSE;
6861 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6862 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6867 SCAN_PLAYFIELD(x, y)
6869 if (AmoebaNr[x][y] == group_nr &&
6870 (Feld[x][y] == EL_AMOEBA_DEAD ||
6871 Feld[x][y] == EL_BD_AMOEBA ||
6872 Feld[x][y] == EL_AMOEBA_GROWING))
6875 Feld[x][y] = new_element;
6876 InitField(x, y, FALSE);
6877 DrawLevelField(x, y);
6883 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6884 SND_BD_AMOEBA_TURNING_TO_ROCK :
6885 SND_BD_AMOEBA_TURNING_TO_GEM));
6888 void AmoebeWaechst(int x, int y)
6890 static unsigned long sound_delay = 0;
6891 static unsigned long sound_delay_value = 0;
6893 if (!MovDelay[x][y]) /* start new growing cycle */
6897 if (DelayReached(&sound_delay, sound_delay_value))
6899 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6900 sound_delay_value = 30;
6904 if (MovDelay[x][y]) /* wait some time before growing bigger */
6907 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6909 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6910 6 - MovDelay[x][y]);
6912 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6915 if (!MovDelay[x][y])
6917 Feld[x][y] = Store[x][y];
6919 DrawLevelField(x, y);
6924 void AmoebaDisappearing(int x, int y)
6926 static unsigned long sound_delay = 0;
6927 static unsigned long sound_delay_value = 0;
6929 if (!MovDelay[x][y]) /* start new shrinking cycle */
6933 if (DelayReached(&sound_delay, sound_delay_value))
6934 sound_delay_value = 30;
6937 if (MovDelay[x][y]) /* wait some time before shrinking */
6940 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6942 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6943 6 - MovDelay[x][y]);
6945 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6948 if (!MovDelay[x][y])
6950 Feld[x][y] = EL_EMPTY;
6951 DrawLevelField(x, y);
6953 /* don't let mole enter this field in this cycle;
6954 (give priority to objects falling to this field from above) */
6960 void AmoebeAbleger(int ax, int ay)
6963 int element = Feld[ax][ay];
6964 int graphic = el2img(element);
6965 int newax = ax, neway = ay;
6966 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6967 static int xy[4][2] =
6975 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6977 Feld[ax][ay] = EL_AMOEBA_DEAD;
6978 DrawLevelField(ax, ay);
6982 if (IS_ANIMATED(graphic))
6983 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6985 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6986 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6988 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6991 if (MovDelay[ax][ay])
6995 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6998 int x = ax + xy[start][0];
6999 int y = ay + xy[start][1];
7001 if (!IN_LEV_FIELD(x, y))
7004 if (IS_FREE(x, y) ||
7005 CAN_GROW_INTO(Feld[x][y]) ||
7006 Feld[x][y] == EL_QUICKSAND_EMPTY)
7012 if (newax == ax && neway == ay)
7015 else /* normal or "filled" (BD style) amoeba */
7018 boolean waiting_for_player = FALSE;
7020 for (i = 0; i < NUM_DIRECTIONS; i++)
7022 int j = (start + i) % 4;
7023 int x = ax + xy[j][0];
7024 int y = ay + xy[j][1];
7026 if (!IN_LEV_FIELD(x, y))
7029 if (IS_FREE(x, y) ||
7030 CAN_GROW_INTO(Feld[x][y]) ||
7031 Feld[x][y] == EL_QUICKSAND_EMPTY)
7037 else if (IS_PLAYER(x, y))
7038 waiting_for_player = TRUE;
7041 if (newax == ax && neway == ay) /* amoeba cannot grow */
7043 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7045 Feld[ax][ay] = EL_AMOEBA_DEAD;
7046 DrawLevelField(ax, ay);
7047 AmoebaCnt[AmoebaNr[ax][ay]]--;
7049 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7051 if (element == EL_AMOEBA_FULL)
7052 AmoebeUmwandeln(ax, ay);
7053 else if (element == EL_BD_AMOEBA)
7054 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7059 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7061 /* amoeba gets larger by growing in some direction */
7063 int new_group_nr = AmoebaNr[ax][ay];
7066 if (new_group_nr == 0)
7068 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7069 printf("AmoebeAbleger(): This should never happen!\n");
7074 AmoebaNr[newax][neway] = new_group_nr;
7075 AmoebaCnt[new_group_nr]++;
7076 AmoebaCnt2[new_group_nr]++;
7078 /* if amoeba touches other amoeba(s) after growing, unify them */
7079 AmoebenVereinigen(newax, neway);
7081 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7083 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7089 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7090 (neway == lev_fieldy - 1 && newax != ax))
7092 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7093 Store[newax][neway] = element;
7095 else if (neway == ay || element == EL_EMC_DRIPPER)
7097 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7099 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7103 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7104 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7105 Store[ax][ay] = EL_AMOEBA_DROP;
7106 ContinueMoving(ax, ay);
7110 DrawLevelField(newax, neway);
7113 void Life(int ax, int ay)
7117 int element = Feld[ax][ay];
7118 int graphic = el2img(element);
7119 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7121 boolean changed = FALSE;
7123 if (IS_ANIMATED(graphic))
7124 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7129 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7130 MovDelay[ax][ay] = life_time;
7132 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7135 if (MovDelay[ax][ay])
7139 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7141 int xx = ax+x1, yy = ay+y1;
7144 if (!IN_LEV_FIELD(xx, yy))
7147 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7149 int x = xx+x2, y = yy+y2;
7151 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7154 if (((Feld[x][y] == element ||
7155 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7157 (IS_FREE(x, y) && Stop[x][y]))
7161 if (xx == ax && yy == ay) /* field in the middle */
7163 if (nachbarn < life_parameter[0] ||
7164 nachbarn > life_parameter[1])
7166 Feld[xx][yy] = EL_EMPTY;
7168 DrawLevelField(xx, yy);
7169 Stop[xx][yy] = TRUE;
7173 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7174 { /* free border field */
7175 if (nachbarn >= life_parameter[2] &&
7176 nachbarn <= life_parameter[3])
7178 Feld[xx][yy] = element;
7179 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7181 DrawLevelField(xx, yy);
7182 Stop[xx][yy] = TRUE;
7189 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7190 SND_GAME_OF_LIFE_GROWING);
7193 static void InitRobotWheel(int x, int y)
7195 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7198 static void RunRobotWheel(int x, int y)
7200 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7203 static void StopRobotWheel(int x, int y)
7205 if (ZX == x && ZY == y)
7209 static void InitTimegateWheel(int x, int y)
7211 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7214 static void RunTimegateWheel(int x, int y)
7216 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7219 static void InitMagicBallDelay(int x, int y)
7222 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7224 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7228 static void ActivateMagicBall(int bx, int by)
7232 if (level.ball_random)
7234 int pos_border = RND(8); /* select one of the eight border elements */
7235 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7236 int xx = pos_content % 3;
7237 int yy = pos_content / 3;
7242 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7243 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7247 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7249 int xx = x - bx + 1;
7250 int yy = y - by + 1;
7252 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7253 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7257 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7260 void CheckExit(int x, int y)
7262 if (local_player->gems_still_needed > 0 ||
7263 local_player->sokobanfields_still_needed > 0 ||
7264 local_player->lights_still_needed > 0)
7266 int element = Feld[x][y];
7267 int graphic = el2img(element);
7269 if (IS_ANIMATED(graphic))
7270 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7275 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7278 Feld[x][y] = EL_EXIT_OPENING;
7280 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7283 void CheckExitSP(int x, int y)
7285 if (local_player->gems_still_needed > 0)
7287 int element = Feld[x][y];
7288 int graphic = el2img(element);
7290 if (IS_ANIMATED(graphic))
7291 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7296 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7299 Feld[x][y] = EL_SP_EXIT_OPENING;
7301 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7304 static void CloseAllOpenTimegates()
7308 SCAN_PLAYFIELD(x, y)
7310 int element = Feld[x][y];
7312 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7314 Feld[x][y] = EL_TIMEGATE_CLOSING;
7316 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7321 void DrawTwinkleOnField(int x, int y)
7323 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7326 if (Feld[x][y] == EL_BD_DIAMOND)
7329 if (MovDelay[x][y] == 0) /* next animation frame */
7330 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7332 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7336 if (setup.direct_draw && MovDelay[x][y])
7337 SetDrawtoField(DRAW_BUFFERED);
7339 DrawLevelElementAnimation(x, y, Feld[x][y]);
7341 if (MovDelay[x][y] != 0)
7343 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7344 10 - MovDelay[x][y]);
7346 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7348 if (setup.direct_draw)
7352 dest_x = FX + SCREENX(x) * TILEX;
7353 dest_y = FY + SCREENY(y) * TILEY;
7355 BlitBitmap(drawto_field, window,
7356 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7357 SetDrawtoField(DRAW_DIRECT);
7363 void MauerWaechst(int x, int y)
7367 if (!MovDelay[x][y]) /* next animation frame */
7368 MovDelay[x][y] = 3 * delay;
7370 if (MovDelay[x][y]) /* wait some time before next frame */
7374 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7376 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7377 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7379 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7382 if (!MovDelay[x][y])
7384 if (MovDir[x][y] == MV_LEFT)
7386 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7387 DrawLevelField(x - 1, y);
7389 else if (MovDir[x][y] == MV_RIGHT)
7391 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7392 DrawLevelField(x + 1, y);
7394 else if (MovDir[x][y] == MV_UP)
7396 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7397 DrawLevelField(x, y - 1);
7401 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7402 DrawLevelField(x, y + 1);
7405 Feld[x][y] = Store[x][y];
7407 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7408 DrawLevelField(x, y);
7413 void MauerAbleger(int ax, int ay)
7415 int element = Feld[ax][ay];
7416 int graphic = el2img(element);
7417 boolean oben_frei = FALSE, unten_frei = FALSE;
7418 boolean links_frei = FALSE, rechts_frei = FALSE;
7419 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7420 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7421 boolean new_wall = FALSE;
7423 if (IS_ANIMATED(graphic))
7424 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7426 if (!MovDelay[ax][ay]) /* start building new wall */
7427 MovDelay[ax][ay] = 6;
7429 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7432 if (MovDelay[ax][ay])
7436 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7438 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7440 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7442 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7445 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7446 element == EL_EXPANDABLE_WALL_ANY)
7450 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7451 Store[ax][ay-1] = element;
7452 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7453 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7454 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7455 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7460 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7461 Store[ax][ay+1] = element;
7462 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7463 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7464 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7465 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7470 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7471 element == EL_EXPANDABLE_WALL_ANY ||
7472 element == EL_EXPANDABLE_WALL ||
7473 element == EL_BD_EXPANDABLE_WALL)
7477 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7478 Store[ax-1][ay] = element;
7479 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7480 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7481 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7482 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7488 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7489 Store[ax+1][ay] = element;
7490 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7491 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7492 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7493 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7498 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7499 DrawLevelField(ax, ay);
7501 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7503 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7504 unten_massiv = TRUE;
7505 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7506 links_massiv = TRUE;
7507 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7508 rechts_massiv = TRUE;
7510 if (((oben_massiv && unten_massiv) ||
7511 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7512 element == EL_EXPANDABLE_WALL) &&
7513 ((links_massiv && rechts_massiv) ||
7514 element == EL_EXPANDABLE_WALL_VERTICAL))
7515 Feld[ax][ay] = EL_WALL;
7518 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7521 void CheckForDragon(int x, int y)
7524 boolean dragon_found = FALSE;
7525 static int xy[4][2] =
7533 for (i = 0; i < NUM_DIRECTIONS; i++)
7535 for (j = 0; j < 4; j++)
7537 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7539 if (IN_LEV_FIELD(xx, yy) &&
7540 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7542 if (Feld[xx][yy] == EL_DRAGON)
7543 dragon_found = TRUE;
7552 for (i = 0; i < NUM_DIRECTIONS; i++)
7554 for (j = 0; j < 3; j++)
7556 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7558 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7560 Feld[xx][yy] = EL_EMPTY;
7561 DrawLevelField(xx, yy);
7570 static void InitBuggyBase(int x, int y)
7572 int element = Feld[x][y];
7573 int activating_delay = FRAMES_PER_SECOND / 4;
7576 (element == EL_SP_BUGGY_BASE ?
7577 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7578 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7580 element == EL_SP_BUGGY_BASE_ACTIVE ?
7581 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7584 static void WarnBuggyBase(int x, int y)
7587 static int xy[4][2] =
7595 for (i = 0; i < NUM_DIRECTIONS; i++)
7597 int xx = x + xy[i][0];
7598 int yy = y + xy[i][1];
7600 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7602 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7609 static void InitTrap(int x, int y)
7611 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7614 static void ActivateTrap(int x, int y)
7616 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7619 static void ChangeActiveTrap(int x, int y)
7621 int graphic = IMG_TRAP_ACTIVE;
7623 /* if new animation frame was drawn, correct crumbled sand border */
7624 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7625 DrawLevelFieldCrumbledSand(x, y);
7628 static int getSpecialActionElement(int element, int number, int base_element)
7630 return (element != EL_EMPTY ? element :
7631 number != -1 ? base_element + number - 1 :
7635 static int getModifiedActionNumber(int value_old, int operator, int operand,
7636 int value_min, int value_max)
7638 int value_new = (operator == CA_MODE_SET ? operand :
7639 operator == CA_MODE_ADD ? value_old + operand :
7640 operator == CA_MODE_SUBTRACT ? value_old - operand :
7641 operator == CA_MODE_MULTIPLY ? value_old * operand :
7642 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7643 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7646 return (value_new < value_min ? value_min :
7647 value_new > value_max ? value_max :
7651 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7653 struct ElementInfo *ei = &element_info[element];
7654 struct ElementChangeInfo *change = &ei->change_page[page];
7655 int target_element = change->target_element;
7656 int action_type = change->action_type;
7657 int action_mode = change->action_mode;
7658 int action_arg = change->action_arg;
7661 if (!change->has_action)
7664 /* ---------- determine action paramater values -------------------------- */
7666 int level_time_value =
7667 (level.time > 0 ? TimeLeft :
7670 int action_arg_element =
7671 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7672 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7673 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7676 int action_arg_direction =
7677 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7678 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7679 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7680 change->actual_trigger_side :
7681 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7682 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7685 int action_arg_number_min =
7686 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7689 int action_arg_number_max =
7690 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7691 action_type == CA_SET_LEVEL_GEMS ? 999 :
7692 action_type == CA_SET_LEVEL_TIME ? 9999 :
7693 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7694 action_type == CA_SET_CE_VALUE ? 9999 :
7695 action_type == CA_SET_CE_SCORE ? 9999 :
7698 int action_arg_number_reset =
7699 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7700 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7701 action_type == CA_SET_LEVEL_TIME ? level.time :
7702 action_type == CA_SET_LEVEL_SCORE ? 0 :
7703 #if USE_NEW_CUSTOM_VALUE
7704 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7706 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7708 action_type == CA_SET_CE_SCORE ? 0 :
7711 int action_arg_number =
7712 (action_arg <= CA_ARG_MAX ? action_arg :
7713 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7714 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7715 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7716 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7717 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7718 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7719 #if USE_NEW_CUSTOM_VALUE
7720 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7722 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7724 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7725 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7726 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7727 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7728 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7729 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7730 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7731 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7732 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7733 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7734 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7737 int action_arg_number_old =
7738 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7739 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7740 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7741 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7742 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7745 int action_arg_number_new =
7746 getModifiedActionNumber(action_arg_number_old,
7747 action_mode, action_arg_number,
7748 action_arg_number_min, action_arg_number_max);
7750 int trigger_player_bits =
7751 (change->actual_trigger_player >= EL_PLAYER_1 &&
7752 change->actual_trigger_player <= EL_PLAYER_4 ?
7753 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7756 int action_arg_player_bits =
7757 (action_arg >= CA_ARG_PLAYER_1 &&
7758 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7759 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7762 /* ---------- execute action -------------------------------------------- */
7764 switch (action_type)
7771 /* ---------- level actions ------------------------------------------- */
7773 case CA_RESTART_LEVEL:
7775 game.restart_level = TRUE;
7780 case CA_SHOW_ENVELOPE:
7782 int element = getSpecialActionElement(action_arg_element,
7783 action_arg_number, EL_ENVELOPE_1);
7785 if (IS_ENVELOPE(element))
7786 local_player->show_envelope = element;
7791 case CA_SET_LEVEL_TIME:
7793 if (level.time > 0) /* only modify limited time value */
7795 TimeLeft = action_arg_number_new;
7797 DrawGameValue_Time(TimeLeft);
7799 if (!TimeLeft && setup.time_limit)
7800 for (i = 0; i < MAX_PLAYERS; i++)
7801 KillPlayer(&stored_player[i]);
7807 case CA_SET_LEVEL_SCORE:
7809 local_player->score = action_arg_number_new;
7811 DrawGameValue_Score(local_player->score);
7816 case CA_SET_LEVEL_GEMS:
7818 local_player->gems_still_needed = action_arg_number_new;
7820 DrawGameValue_Emeralds(local_player->gems_still_needed);
7825 #if !USE_PLAYER_GRAVITY
7826 case CA_SET_LEVEL_GRAVITY:
7828 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7829 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7830 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7836 case CA_SET_LEVEL_WIND:
7838 game.wind_direction = action_arg_direction;
7843 /* ---------- player actions ------------------------------------------ */
7845 case CA_MOVE_PLAYER:
7847 /* automatically move to the next field in specified direction */
7848 for (i = 0; i < MAX_PLAYERS; i++)
7849 if (trigger_player_bits & (1 << i))
7850 stored_player[i].programmed_action = action_arg_direction;
7855 case CA_EXIT_PLAYER:
7857 for (i = 0; i < MAX_PLAYERS; i++)
7858 if (action_arg_player_bits & (1 << i))
7859 PlayerWins(&stored_player[i]);
7864 case CA_KILL_PLAYER:
7866 for (i = 0; i < MAX_PLAYERS; i++)
7867 if (action_arg_player_bits & (1 << i))
7868 KillPlayer(&stored_player[i]);
7873 case CA_SET_PLAYER_KEYS:
7875 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7876 int element = getSpecialActionElement(action_arg_element,
7877 action_arg_number, EL_KEY_1);
7879 if (IS_KEY(element))
7881 for (i = 0; i < MAX_PLAYERS; i++)
7883 if (trigger_player_bits & (1 << i))
7885 stored_player[i].key[KEY_NR(element)] = key_state;
7887 DrawGameDoorValues();
7895 case CA_SET_PLAYER_SPEED:
7897 for (i = 0; i < MAX_PLAYERS; i++)
7899 if (trigger_player_bits & (1 << i))
7901 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7903 if (action_arg == CA_ARG_SPEED_FASTER &&
7904 stored_player[i].cannot_move)
7906 action_arg_number = STEPSIZE_VERY_SLOW;
7908 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7909 action_arg == CA_ARG_SPEED_FASTER)
7911 action_arg_number = 2;
7912 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7915 else if (action_arg == CA_ARG_NUMBER_RESET)
7917 action_arg_number = level.initial_player_stepsize[i];
7921 getModifiedActionNumber(move_stepsize,
7924 action_arg_number_min,
7925 action_arg_number_max);
7927 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7934 case CA_SET_PLAYER_SHIELD:
7936 for (i = 0; i < MAX_PLAYERS; i++)
7938 if (trigger_player_bits & (1 << i))
7940 if (action_arg == CA_ARG_SHIELD_OFF)
7942 stored_player[i].shield_normal_time_left = 0;
7943 stored_player[i].shield_deadly_time_left = 0;
7945 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7947 stored_player[i].shield_normal_time_left = 999999;
7949 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7951 stored_player[i].shield_normal_time_left = 999999;
7952 stored_player[i].shield_deadly_time_left = 999999;
7960 #if USE_PLAYER_GRAVITY
7961 case CA_SET_PLAYER_GRAVITY:
7963 for (i = 0; i < MAX_PLAYERS; i++)
7965 if (trigger_player_bits & (1 << i))
7967 stored_player[i].gravity =
7968 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7969 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7970 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7971 stored_player[i].gravity);
7979 case CA_SET_PLAYER_ARTWORK:
7981 for (i = 0; i < MAX_PLAYERS; i++)
7983 if (trigger_player_bits & (1 << i))
7985 int artwork_element = action_arg_element;
7987 if (action_arg == CA_ARG_ELEMENT_RESET)
7989 (level.use_artwork_element[i] ? level.artwork_element[i] :
7990 stored_player[i].element_nr);
7992 #if USE_GFX_RESET_PLAYER_ARTWORK
7993 if (stored_player[i].artwork_element != artwork_element)
7994 stored_player[i].Frame = 0;
7997 stored_player[i].artwork_element = artwork_element;
7999 SetPlayerWaiting(&stored_player[i], FALSE);
8001 /* set number of special actions for bored and sleeping animation */
8002 stored_player[i].num_special_action_bored =
8003 get_num_special_action(artwork_element,
8004 ACTION_BORING_1, ACTION_BORING_LAST);
8005 stored_player[i].num_special_action_sleeping =
8006 get_num_special_action(artwork_element,
8007 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8014 /* ---------- CE actions ---------------------------------------------- */
8016 case CA_SET_CE_VALUE:
8018 #if USE_NEW_CUSTOM_VALUE
8019 int last_ce_value = CustomValue[x][y];
8021 CustomValue[x][y] = action_arg_number_new;
8023 if (CustomValue[x][y] != last_ce_value)
8025 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8026 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8028 if (CustomValue[x][y] == 0)
8030 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8031 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8039 case CA_SET_CE_SCORE:
8041 #if USE_NEW_CUSTOM_VALUE
8042 int last_ce_score = ei->collect_score;
8044 ei->collect_score = action_arg_number_new;
8046 if (ei->collect_score != last_ce_score)
8048 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8049 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8051 if (ei->collect_score == 0)
8055 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8056 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8059 This is a very special case that seems to be a mixture between
8060 CheckElementChange() and CheckTriggeredElementChange(): while
8061 the first one only affects single elements that are triggered
8062 directly, the second one affects multiple elements in the playfield
8063 that are triggered indirectly by another element. This is a third
8064 case: Changing the CE score always affects multiple identical CEs,
8065 so every affected CE must be checked, not only the single CE for
8066 which the CE score was changed in the first place (as every instance
8067 of that CE shares the same CE score, and therefore also can change)!
8069 SCAN_PLAYFIELD(xx, yy)
8071 if (Feld[xx][yy] == element)
8072 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8073 CE_SCORE_GETS_ZERO);
8082 /* ---------- engine actions ------------------------------------------ */
8084 case CA_SET_ENGINE_SCAN_MODE:
8086 InitPlayfieldScanMode(action_arg);
8096 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8098 int old_element = Feld[x][y];
8099 int new_element = get_element_from_group_element(element);
8100 int previous_move_direction = MovDir[x][y];
8101 #if USE_NEW_CUSTOM_VALUE
8102 int last_ce_value = CustomValue[x][y];
8104 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8105 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8106 boolean add_player_onto_element = (new_element_is_player &&
8107 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8108 /* this breaks SnakeBite when a snake is
8109 halfway through a door that closes */
8110 /* NOW FIXED AT LEVEL INIT IN files.c */
8111 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8113 IS_WALKABLE(old_element));
8116 /* check if element under the player changes from accessible to unaccessible
8117 (needed for special case of dropping element which then changes) */
8118 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8119 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8127 if (!add_player_onto_element)
8129 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8130 RemoveMovingField(x, y);
8134 Feld[x][y] = new_element;
8136 #if !USE_GFX_RESET_GFX_ANIMATION
8137 ResetGfxAnimation(x, y);
8138 ResetRandomAnimationValue(x, y);
8141 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8142 MovDir[x][y] = previous_move_direction;
8144 #if USE_NEW_CUSTOM_VALUE
8145 if (element_info[new_element].use_last_ce_value)
8146 CustomValue[x][y] = last_ce_value;
8149 InitField_WithBug1(x, y, FALSE);
8151 new_element = Feld[x][y]; /* element may have changed */
8153 #if USE_GFX_RESET_GFX_ANIMATION
8154 ResetGfxAnimation(x, y);
8155 ResetRandomAnimationValue(x, y);
8158 DrawLevelField(x, y);
8160 if (GFX_CRUMBLED(new_element))
8161 DrawLevelFieldCrumbledSandNeighbours(x, y);
8165 /* check if element under the player changes from accessible to unaccessible
8166 (needed for special case of dropping element which then changes) */
8167 /* (must be checked after creating new element for walkable group elements) */
8168 #if USE_FIX_KILLED_BY_NON_WALKABLE
8169 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8170 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8177 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8178 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8187 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8188 if (new_element_is_player)
8189 RelocatePlayer(x, y, new_element);
8192 ChangeCount[x][y]++; /* count number of changes in the same frame */
8194 TestIfBadThingTouchesPlayer(x, y);
8195 TestIfPlayerTouchesCustomElement(x, y);
8196 TestIfElementTouchesCustomElement(x, y);
8199 static void CreateField(int x, int y, int element)
8201 CreateFieldExt(x, y, element, FALSE);
8204 static void CreateElementFromChange(int x, int y, int element)
8206 element = GET_VALID_RUNTIME_ELEMENT(element);
8208 #if USE_STOP_CHANGED_ELEMENTS
8209 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8211 int old_element = Feld[x][y];
8213 /* prevent changed element from moving in same engine frame
8214 unless both old and new element can either fall or move */
8215 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8216 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8221 CreateFieldExt(x, y, element, TRUE);
8224 static boolean ChangeElement(int x, int y, int element, int page)
8226 struct ElementInfo *ei = &element_info[element];
8227 struct ElementChangeInfo *change = &ei->change_page[page];
8228 int ce_value = CustomValue[x][y];
8229 int ce_score = ei->collect_score;
8231 int old_element = Feld[x][y];
8233 /* always use default change event to prevent running into a loop */
8234 if (ChangeEvent[x][y] == -1)
8235 ChangeEvent[x][y] = CE_DELAY;
8237 if (ChangeEvent[x][y] == CE_DELAY)
8239 /* reset actual trigger element, trigger player and action element */
8240 change->actual_trigger_element = EL_EMPTY;
8241 change->actual_trigger_player = EL_PLAYER_1;
8242 change->actual_trigger_side = CH_SIDE_NONE;
8243 change->actual_trigger_ce_value = 0;
8244 change->actual_trigger_ce_score = 0;
8247 /* do not change elements more than a specified maximum number of changes */
8248 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8251 ChangeCount[x][y]++; /* count number of changes in the same frame */
8253 if (change->explode)
8260 if (change->use_target_content)
8262 boolean complete_replace = TRUE;
8263 boolean can_replace[3][3];
8266 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8269 boolean is_walkable;
8270 boolean is_diggable;
8271 boolean is_collectible;
8272 boolean is_removable;
8273 boolean is_destructible;
8274 int ex = x + xx - 1;
8275 int ey = y + yy - 1;
8276 int content_element = change->target_content.e[xx][yy];
8279 can_replace[xx][yy] = TRUE;
8281 if (ex == x && ey == y) /* do not check changing element itself */
8284 if (content_element == EL_EMPTY_SPACE)
8286 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8291 if (!IN_LEV_FIELD(ex, ey))
8293 can_replace[xx][yy] = FALSE;
8294 complete_replace = FALSE;
8301 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8302 e = MovingOrBlocked2Element(ex, ey);
8304 is_empty = (IS_FREE(ex, ey) ||
8305 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8307 is_walkable = (is_empty || IS_WALKABLE(e));
8308 is_diggable = (is_empty || IS_DIGGABLE(e));
8309 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8310 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8311 is_removable = (is_diggable || is_collectible);
8313 can_replace[xx][yy] =
8314 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8315 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8316 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8317 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8318 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8319 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8320 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8322 if (!can_replace[xx][yy])
8323 complete_replace = FALSE;
8326 if (!change->only_if_complete || complete_replace)
8328 boolean something_has_changed = FALSE;
8330 if (change->only_if_complete && change->use_random_replace &&
8331 RND(100) < change->random_percentage)
8334 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8336 int ex = x + xx - 1;
8337 int ey = y + yy - 1;
8338 int content_element;
8340 if (can_replace[xx][yy] && (!change->use_random_replace ||
8341 RND(100) < change->random_percentage))
8343 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8344 RemoveMovingField(ex, ey);
8346 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8348 content_element = change->target_content.e[xx][yy];
8349 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8350 ce_value, ce_score);
8352 CreateElementFromChange(ex, ey, target_element);
8354 something_has_changed = TRUE;
8356 /* for symmetry reasons, freeze newly created border elements */
8357 if (ex != x || ey != y)
8358 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8362 if (something_has_changed)
8364 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8365 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8371 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8372 ce_value, ce_score);
8374 if (element == EL_DIAGONAL_GROWING ||
8375 element == EL_DIAGONAL_SHRINKING)
8377 target_element = Store[x][y];
8379 Store[x][y] = EL_EMPTY;
8382 CreateElementFromChange(x, y, target_element);
8384 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8385 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8388 /* this uses direct change before indirect change */
8389 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8394 #if USE_NEW_DELAYED_ACTION
8396 static void HandleElementChange(int x, int y, int page)
8398 int element = MovingOrBlocked2Element(x, y);
8399 struct ElementInfo *ei = &element_info[element];
8400 struct ElementChangeInfo *change = &ei->change_page[page];
8403 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8404 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8407 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8408 x, y, element, element_info[element].token_name);
8409 printf("HandleElementChange(): This should never happen!\n");
8414 /* this can happen with classic bombs on walkable, changing elements */
8415 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8418 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8419 ChangeDelay[x][y] = 0;
8425 if (ChangeDelay[x][y] == 0) /* initialize element change */
8427 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8429 if (change->can_change)
8431 ResetGfxAnimation(x, y);
8432 ResetRandomAnimationValue(x, y);
8434 if (change->pre_change_function)
8435 change->pre_change_function(x, y);
8439 ChangeDelay[x][y]--;
8441 if (ChangeDelay[x][y] != 0) /* continue element change */
8443 if (change->can_change)
8445 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8447 if (IS_ANIMATED(graphic))
8448 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8450 if (change->change_function)
8451 change->change_function(x, y);
8454 else /* finish element change */
8456 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8458 page = ChangePage[x][y];
8459 ChangePage[x][y] = -1;
8461 change = &ei->change_page[page];
8464 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8466 ChangeDelay[x][y] = 1; /* try change after next move step */
8467 ChangePage[x][y] = page; /* remember page to use for change */
8472 if (change->can_change)
8474 if (ChangeElement(x, y, element, page))
8476 if (change->post_change_function)
8477 change->post_change_function(x, y);
8481 if (change->has_action)
8482 ExecuteCustomElementAction(x, y, element, page);
8488 static void HandleElementChange(int x, int y, int page)
8490 int element = MovingOrBlocked2Element(x, y);
8491 struct ElementInfo *ei = &element_info[element];
8492 struct ElementChangeInfo *change = &ei->change_page[page];
8495 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8498 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8499 x, y, element, element_info[element].token_name);
8500 printf("HandleElementChange(): This should never happen!\n");
8505 /* this can happen with classic bombs on walkable, changing elements */
8506 if (!CAN_CHANGE(element))
8509 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8510 ChangeDelay[x][y] = 0;
8516 if (ChangeDelay[x][y] == 0) /* initialize element change */
8518 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8520 ResetGfxAnimation(x, y);
8521 ResetRandomAnimationValue(x, y);
8523 if (change->pre_change_function)
8524 change->pre_change_function(x, y);
8527 ChangeDelay[x][y]--;
8529 if (ChangeDelay[x][y] != 0) /* continue element change */
8531 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8533 if (IS_ANIMATED(graphic))
8534 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8536 if (change->change_function)
8537 change->change_function(x, y);
8539 else /* finish element change */
8541 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8543 page = ChangePage[x][y];
8544 ChangePage[x][y] = -1;
8546 change = &ei->change_page[page];
8549 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8551 ChangeDelay[x][y] = 1; /* try change after next move step */
8552 ChangePage[x][y] = page; /* remember page to use for change */
8557 if (ChangeElement(x, y, element, page))
8559 if (change->post_change_function)
8560 change->post_change_function(x, y);
8567 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8568 int trigger_element,
8574 boolean change_done_any = FALSE;
8575 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8578 if (!(trigger_events[trigger_element][trigger_event]))
8581 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8583 int element = EL_CUSTOM_START + i;
8584 boolean change_done = FALSE;
8587 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8588 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8591 for (p = 0; p < element_info[element].num_change_pages; p++)
8593 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8595 if (change->can_change_or_has_action &&
8596 change->has_event[trigger_event] &&
8597 change->trigger_side & trigger_side &&
8598 change->trigger_player & trigger_player &&
8599 change->trigger_page & trigger_page_bits &&
8600 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8602 change->actual_trigger_element = trigger_element;
8603 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8604 change->actual_trigger_side = trigger_side;
8605 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8606 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8608 if ((change->can_change && !change_done) || change->has_action)
8612 SCAN_PLAYFIELD(x, y)
8614 if (Feld[x][y] == element)
8616 if (change->can_change && !change_done)
8618 ChangeDelay[x][y] = 1;
8619 ChangeEvent[x][y] = trigger_event;
8621 HandleElementChange(x, y, p);
8623 #if USE_NEW_DELAYED_ACTION
8624 else if (change->has_action)
8626 ExecuteCustomElementAction(x, y, element, p);
8627 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8630 if (change->has_action)
8632 ExecuteCustomElementAction(x, y, element, p);
8633 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8639 if (change->can_change)
8642 change_done_any = TRUE;
8649 return change_done_any;
8652 static boolean CheckElementChangeExt(int x, int y,
8654 int trigger_element,
8659 boolean change_done = FALSE;
8662 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8663 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8666 if (Feld[x][y] == EL_BLOCKED)
8668 Blocked2Moving(x, y, &x, &y);
8669 element = Feld[x][y];
8673 /* check if element has already changed */
8674 if (Feld[x][y] != element)
8677 /* check if element has already changed or is about to change after moving */
8678 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8679 Feld[x][y] != element) ||
8681 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8682 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8683 ChangePage[x][y] != -1)))
8687 for (p = 0; p < element_info[element].num_change_pages; p++)
8689 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8691 /* check trigger element for all events where the element that is checked
8692 for changing interacts with a directly adjacent element -- this is
8693 different to element changes that affect other elements to change on the
8694 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
8695 boolean check_trigger_element =
8696 (trigger_event == CE_TOUCHING_X ||
8697 trigger_event == CE_HITTING_X ||
8698 trigger_event == CE_HIT_BY_X ||
8700 /* this one was forgotten until 3.2.3 */
8701 trigger_event == CE_DIGGING_X);
8704 if (change->can_change_or_has_action &&
8705 change->has_event[trigger_event] &&
8706 change->trigger_side & trigger_side &&
8707 change->trigger_player & trigger_player &&
8708 (!check_trigger_element ||
8709 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8711 change->actual_trigger_element = trigger_element;
8712 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8713 change->actual_trigger_side = trigger_side;
8714 change->actual_trigger_ce_value = CustomValue[x][y];
8715 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8717 /* special case: trigger element not at (x,y) position for some events */
8718 if (check_trigger_element)
8730 { 0, 0 }, { 0, 0 }, { 0, 0 },
8734 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8735 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8737 change->actual_trigger_ce_value = CustomValue[xx][yy];
8738 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8741 if (change->can_change && !change_done)
8743 ChangeDelay[x][y] = 1;
8744 ChangeEvent[x][y] = trigger_event;
8746 HandleElementChange(x, y, p);
8750 #if USE_NEW_DELAYED_ACTION
8751 else if (change->has_action)
8753 ExecuteCustomElementAction(x, y, element, p);
8754 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8757 if (change->has_action)
8759 ExecuteCustomElementAction(x, y, element, p);
8760 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8769 static void PlayPlayerSound(struct PlayerInfo *player)
8771 int jx = player->jx, jy = player->jy;
8772 int sound_element = player->artwork_element;
8773 int last_action = player->last_action_waiting;
8774 int action = player->action_waiting;
8776 if (player->is_waiting)
8778 if (action != last_action)
8779 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8781 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8785 if (action != last_action)
8786 StopSound(element_info[sound_element].sound[last_action]);
8788 if (last_action == ACTION_SLEEPING)
8789 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8793 static void PlayAllPlayersSound()
8797 for (i = 0; i < MAX_PLAYERS; i++)
8798 if (stored_player[i].active)
8799 PlayPlayerSound(&stored_player[i]);
8802 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8804 boolean last_waiting = player->is_waiting;
8805 int move_dir = player->MovDir;
8807 player->dir_waiting = move_dir;
8808 player->last_action_waiting = player->action_waiting;
8812 if (!last_waiting) /* not waiting -> waiting */
8814 player->is_waiting = TRUE;
8816 player->frame_counter_bored =
8818 game.player_boring_delay_fixed +
8819 GetSimpleRandom(game.player_boring_delay_random);
8820 player->frame_counter_sleeping =
8822 game.player_sleeping_delay_fixed +
8823 GetSimpleRandom(game.player_sleeping_delay_random);
8825 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8828 if (game.player_sleeping_delay_fixed +
8829 game.player_sleeping_delay_random > 0 &&
8830 player->anim_delay_counter == 0 &&
8831 player->post_delay_counter == 0 &&
8832 FrameCounter >= player->frame_counter_sleeping)
8833 player->is_sleeping = TRUE;
8834 else if (game.player_boring_delay_fixed +
8835 game.player_boring_delay_random > 0 &&
8836 FrameCounter >= player->frame_counter_bored)
8837 player->is_bored = TRUE;
8839 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8840 player->is_bored ? ACTION_BORING :
8843 if (player->is_sleeping && player->use_murphy)
8845 /* special case for sleeping Murphy when leaning against non-free tile */
8847 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8848 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8849 !IS_MOVING(player->jx - 1, player->jy)))
8851 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8852 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8853 !IS_MOVING(player->jx + 1, player->jy)))
8854 move_dir = MV_RIGHT;
8856 player->is_sleeping = FALSE;
8858 player->dir_waiting = move_dir;
8861 if (player->is_sleeping)
8863 if (player->num_special_action_sleeping > 0)
8865 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8867 int last_special_action = player->special_action_sleeping;
8868 int num_special_action = player->num_special_action_sleeping;
8869 int special_action =
8870 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8871 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8872 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8873 last_special_action + 1 : ACTION_SLEEPING);
8874 int special_graphic =
8875 el_act_dir2img(player->artwork_element, special_action, move_dir);
8877 player->anim_delay_counter =
8878 graphic_info[special_graphic].anim_delay_fixed +
8879 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8880 player->post_delay_counter =
8881 graphic_info[special_graphic].post_delay_fixed +
8882 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8884 player->special_action_sleeping = special_action;
8887 if (player->anim_delay_counter > 0)
8889 player->action_waiting = player->special_action_sleeping;
8890 player->anim_delay_counter--;
8892 else if (player->post_delay_counter > 0)
8894 player->post_delay_counter--;
8898 else if (player->is_bored)
8900 if (player->num_special_action_bored > 0)
8902 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8904 int special_action =
8905 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8906 int special_graphic =
8907 el_act_dir2img(player->artwork_element, special_action, move_dir);
8909 player->anim_delay_counter =
8910 graphic_info[special_graphic].anim_delay_fixed +
8911 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8912 player->post_delay_counter =
8913 graphic_info[special_graphic].post_delay_fixed +
8914 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8916 player->special_action_bored = special_action;
8919 if (player->anim_delay_counter > 0)
8921 player->action_waiting = player->special_action_bored;
8922 player->anim_delay_counter--;
8924 else if (player->post_delay_counter > 0)
8926 player->post_delay_counter--;
8931 else if (last_waiting) /* waiting -> not waiting */
8933 player->is_waiting = FALSE;
8934 player->is_bored = FALSE;
8935 player->is_sleeping = FALSE;
8937 player->frame_counter_bored = -1;
8938 player->frame_counter_sleeping = -1;
8940 player->anim_delay_counter = 0;
8941 player->post_delay_counter = 0;
8943 player->dir_waiting = player->MovDir;
8944 player->action_waiting = ACTION_DEFAULT;
8946 player->special_action_bored = ACTION_DEFAULT;
8947 player->special_action_sleeping = ACTION_DEFAULT;
8951 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8953 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8954 int left = player_action & JOY_LEFT;
8955 int right = player_action & JOY_RIGHT;
8956 int up = player_action & JOY_UP;
8957 int down = player_action & JOY_DOWN;
8958 int button1 = player_action & JOY_BUTTON_1;
8959 int button2 = player_action & JOY_BUTTON_2;
8960 int dx = (left ? -1 : right ? 1 : 0);
8961 int dy = (up ? -1 : down ? 1 : 0);
8963 if (!player->active || tape.pausing)
8969 snapped = SnapField(player, dx, dy);
8973 dropped = DropElement(player);
8975 moved = MovePlayer(player, dx, dy);
8978 if (tape.single_step && tape.recording && !tape.pausing)
8980 if (button1 || (dropped && !moved))
8982 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8983 SnapField(player, 0, 0); /* stop snapping */
8987 SetPlayerWaiting(player, FALSE);
8989 return player_action;
8993 /* no actions for this player (no input at player's configured device) */
8995 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8996 SnapField(player, 0, 0);
8997 CheckGravityMovementWhenNotMoving(player);
8999 if (player->MovPos == 0)
9000 SetPlayerWaiting(player, TRUE);
9002 if (player->MovPos == 0) /* needed for tape.playing */
9003 player->is_moving = FALSE;
9005 player->is_dropping = FALSE;
9006 player->is_dropping_pressed = FALSE;
9007 player->drop_pressed_delay = 0;
9013 static void CheckLevelTime()
9017 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9019 if (level.native_em_level->lev->home == 0) /* all players at home */
9021 PlayerWins(local_player);
9023 AllPlayersGone = TRUE;
9025 level.native_em_level->lev->home = -1;
9028 if (level.native_em_level->ply[0]->alive == 0 &&
9029 level.native_em_level->ply[1]->alive == 0 &&
9030 level.native_em_level->ply[2]->alive == 0 &&
9031 level.native_em_level->ply[3]->alive == 0) /* all dead */
9032 AllPlayersGone = TRUE;
9035 if (TimeFrames >= FRAMES_PER_SECOND)
9040 for (i = 0; i < MAX_PLAYERS; i++)
9042 struct PlayerInfo *player = &stored_player[i];
9044 if (SHIELD_ON(player))
9046 player->shield_normal_time_left--;
9048 if (player->shield_deadly_time_left > 0)
9049 player->shield_deadly_time_left--;
9053 if (!local_player->LevelSolved && !level.use_step_counter)
9061 if (TimeLeft <= 10 && setup.time_limit)
9062 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9064 DrawGameValue_Time(TimeLeft);
9066 if (!TimeLeft && setup.time_limit)
9068 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9069 level.native_em_level->lev->killed_out_of_time = TRUE;
9071 for (i = 0; i < MAX_PLAYERS; i++)
9072 KillPlayer(&stored_player[i]);
9075 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9076 DrawGameValue_Time(TimePlayed);
9078 level.native_em_level->lev->time =
9079 (level.time == 0 ? TimePlayed : TimeLeft);
9082 if (tape.recording || tape.playing)
9083 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9087 void AdvanceFrameAndPlayerCounters(int player_nr)
9091 /* advance frame counters (global frame counter and time frame counter) */
9095 /* advance player counters (counters for move delay, move animation etc.) */
9096 for (i = 0; i < MAX_PLAYERS; i++)
9098 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9099 int move_delay_value = stored_player[i].move_delay_value;
9100 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9102 if (!advance_player_counters) /* not all players may be affected */
9105 #if USE_NEW_PLAYER_ANIM
9106 if (move_frames == 0) /* less than one move per game frame */
9108 int stepsize = TILEX / move_delay_value;
9109 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9110 int count = (stored_player[i].is_moving ?
9111 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9113 if (count % delay == 0)
9118 stored_player[i].Frame += move_frames;
9120 if (stored_player[i].MovPos != 0)
9121 stored_player[i].StepFrame += move_frames;
9123 if (stored_player[i].move_delay > 0)
9124 stored_player[i].move_delay--;
9126 /* due to bugs in previous versions, counter must count up, not down */
9127 if (stored_player[i].push_delay != -1)
9128 stored_player[i].push_delay++;
9130 if (stored_player[i].drop_delay > 0)
9131 stored_player[i].drop_delay--;
9133 if (stored_player[i].is_dropping_pressed)
9134 stored_player[i].drop_pressed_delay++;
9138 void StartGameActions(boolean init_network_game, boolean record_tape,
9141 unsigned long new_random_seed = InitRND(random_seed);
9144 TapeStartRecording(new_random_seed);
9146 #if defined(NETWORK_AVALIABLE)
9147 if (init_network_game)
9149 SendToServer_StartPlaying();
9160 static unsigned long game_frame_delay = 0;
9161 unsigned long game_frame_delay_value;
9162 byte *recorded_player_action;
9163 byte summarized_player_action = 0;
9164 byte tape_action[MAX_PLAYERS];
9167 if (game.restart_level)
9168 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9170 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9172 if (level.native_em_level->lev->home == 0) /* all players at home */
9174 PlayerWins(local_player);
9176 AllPlayersGone = TRUE;
9178 level.native_em_level->lev->home = -1;
9181 if (level.native_em_level->ply[0]->alive == 0 &&
9182 level.native_em_level->ply[1]->alive == 0 &&
9183 level.native_em_level->ply[2]->alive == 0 &&
9184 level.native_em_level->ply[3]->alive == 0) /* all dead */
9185 AllPlayersGone = TRUE;
9188 if (local_player->LevelSolved)
9191 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9194 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9197 game_frame_delay_value =
9198 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9200 if (tape.playing && tape.warp_forward && !tape.pausing)
9201 game_frame_delay_value = 0;
9203 /* ---------- main game synchronization point ---------- */
9205 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9207 if (network_playing && !network_player_action_received)
9209 /* try to get network player actions in time */
9211 #if defined(NETWORK_AVALIABLE)
9212 /* last chance to get network player actions without main loop delay */
9216 /* game was quit by network peer */
9217 if (game_status != GAME_MODE_PLAYING)
9220 if (!network_player_action_received)
9221 return; /* failed to get network player actions in time */
9223 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9229 /* at this point we know that we really continue executing the game */
9231 network_player_action_received = FALSE;
9233 /* when playing tape, read previously recorded player input from tape data */
9234 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9237 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9242 if (tape.set_centered_player)
9244 game.centered_player_nr_next = tape.centered_player_nr_next;
9245 game.set_centered_player = TRUE;
9248 for (i = 0; i < MAX_PLAYERS; i++)
9250 summarized_player_action |= stored_player[i].action;
9252 if (!network_playing)
9253 stored_player[i].effective_action = stored_player[i].action;
9256 #if defined(NETWORK_AVALIABLE)
9257 if (network_playing)
9258 SendToServer_MovePlayer(summarized_player_action);
9261 if (!options.network && !setup.team_mode)
9262 local_player->effective_action = summarized_player_action;
9264 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9266 for (i = 0; i < MAX_PLAYERS; i++)
9267 stored_player[i].effective_action =
9268 (i == game.centered_player_nr ? summarized_player_action : 0);
9271 if (recorded_player_action != NULL)
9272 for (i = 0; i < MAX_PLAYERS; i++)
9273 stored_player[i].effective_action = recorded_player_action[i];
9275 for (i = 0; i < MAX_PLAYERS; i++)
9277 tape_action[i] = stored_player[i].effective_action;
9279 /* (this can only happen in the R'n'D game engine) */
9280 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9281 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9284 /* only record actions from input devices, but not programmed actions */
9286 TapeRecordAction(tape_action);
9288 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9290 GameActions_EM_Main();
9298 void GameActions_EM_Main()
9300 byte effective_action[MAX_PLAYERS];
9301 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9304 for (i = 0; i < MAX_PLAYERS; i++)
9305 effective_action[i] = stored_player[i].effective_action;
9307 GameActions_EM(effective_action, warp_mode);
9311 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9314 void GameActions_RND()
9316 int magic_wall_x = 0, magic_wall_y = 0;
9317 int i, x, y, element, graphic;
9319 InitPlayfieldScanModeVars();
9321 #if USE_ONE_MORE_CHANGE_PER_FRAME
9322 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9324 SCAN_PLAYFIELD(x, y)
9326 ChangeCount[x][y] = 0;
9327 ChangeEvent[x][y] = -1;
9332 if (game.set_centered_player)
9334 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9336 /* switching to "all players" only possible if all players fit to screen */
9337 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9339 game.centered_player_nr_next = game.centered_player_nr;
9340 game.set_centered_player = FALSE;
9343 /* do not switch focus to non-existing (or non-active) player */
9344 if (game.centered_player_nr_next >= 0 &&
9345 !stored_player[game.centered_player_nr_next].active)
9347 game.centered_player_nr_next = game.centered_player_nr;
9348 game.set_centered_player = FALSE;
9352 if (game.set_centered_player &&
9353 ScreenMovPos == 0) /* screen currently aligned at tile position */
9357 if (game.centered_player_nr_next == -1)
9359 setScreenCenteredToAllPlayers(&sx, &sy);
9363 sx = stored_player[game.centered_player_nr_next].jx;
9364 sy = stored_player[game.centered_player_nr_next].jy;
9367 game.centered_player_nr = game.centered_player_nr_next;
9368 game.set_centered_player = FALSE;
9370 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
9371 DrawGameDoorValues();
9374 for (i = 0; i < MAX_PLAYERS; i++)
9376 int actual_player_action = stored_player[i].effective_action;
9379 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9380 - rnd_equinox_tetrachloride 048
9381 - rnd_equinox_tetrachloride_ii 096
9382 - rnd_emanuel_schmieg 002
9383 - doctor_sloan_ww 001, 020
9385 if (stored_player[i].MovPos == 0)
9386 CheckGravityMovement(&stored_player[i]);
9389 /* overwrite programmed action with tape action */
9390 if (stored_player[i].programmed_action)
9391 actual_player_action = stored_player[i].programmed_action;
9393 PlayerActions(&stored_player[i], actual_player_action);
9395 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9398 ScrollScreen(NULL, SCROLL_GO_ON);
9400 /* for backwards compatibility, the following code emulates a fixed bug that
9401 occured when pushing elements (causing elements that just made their last
9402 pushing step to already (if possible) make their first falling step in the
9403 same game frame, which is bad); this code is also needed to use the famous
9404 "spring push bug" which is used in older levels and might be wanted to be
9405 used also in newer levels, but in this case the buggy pushing code is only
9406 affecting the "spring" element and no other elements */
9408 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9410 for (i = 0; i < MAX_PLAYERS; i++)
9412 struct PlayerInfo *player = &stored_player[i];
9416 if (player->active && player->is_pushing && player->is_moving &&
9418 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9419 Feld[x][y] == EL_SPRING))
9421 ContinueMoving(x, y);
9423 /* continue moving after pushing (this is actually a bug) */
9424 if (!IS_MOVING(x, y))
9432 SCAN_PLAYFIELD(x, y)
9434 ChangeCount[x][y] = 0;
9435 ChangeEvent[x][y] = -1;
9437 /* this must be handled before main playfield loop */
9438 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9441 if (MovDelay[x][y] <= 0)
9445 #if USE_NEW_SNAP_DELAY
9446 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9449 if (MovDelay[x][y] <= 0)
9452 DrawLevelField(x, y);
9454 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9460 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9462 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9463 printf("GameActions(): This should never happen!\n");
9465 ChangePage[x][y] = -1;
9470 if (WasJustMoving[x][y] > 0)
9471 WasJustMoving[x][y]--;
9472 if (WasJustFalling[x][y] > 0)
9473 WasJustFalling[x][y]--;
9474 if (CheckCollision[x][y] > 0)
9475 CheckCollision[x][y]--;
9476 if (CheckImpact[x][y] > 0)
9477 CheckImpact[x][y]--;
9481 /* reset finished pushing action (not done in ContinueMoving() to allow
9482 continuous pushing animation for elements with zero push delay) */
9483 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9485 ResetGfxAnimation(x, y);
9486 DrawLevelField(x, y);
9490 if (IS_BLOCKED(x, y))
9494 Blocked2Moving(x, y, &oldx, &oldy);
9495 if (!IS_MOVING(oldx, oldy))
9497 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9498 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9499 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9500 printf("GameActions(): This should never happen!\n");
9506 SCAN_PLAYFIELD(x, y)
9508 element = Feld[x][y];
9509 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9511 ResetGfxFrame(x, y, TRUE);
9513 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9514 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9515 ResetRandomAnimationValue(x, y);
9517 SetRandomAnimationValue(x, y);
9519 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9521 if (IS_INACTIVE(element))
9523 if (IS_ANIMATED(graphic))
9524 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9529 /* this may take place after moving, so 'element' may have changed */
9530 if (IS_CHANGING(x, y) &&
9531 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9533 int page = element_info[element].event_page_nr[CE_DELAY];
9536 HandleElementChange(x, y, page);
9538 if (CAN_CHANGE(element))
9539 HandleElementChange(x, y, page);
9541 if (HAS_ACTION(element))
9542 ExecuteCustomElementAction(x, y, element, page);
9545 element = Feld[x][y];
9546 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9549 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9553 element = Feld[x][y];
9554 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9556 if (IS_ANIMATED(graphic) &&
9559 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9561 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9562 DrawTwinkleOnField(x, y);
9564 else if ((element == EL_ACID ||
9565 element == EL_EXIT_OPEN ||
9566 element == EL_SP_EXIT_OPEN ||
9567 element == EL_SP_TERMINAL ||
9568 element == EL_SP_TERMINAL_ACTIVE ||
9569 element == EL_EXTRA_TIME ||
9570 element == EL_SHIELD_NORMAL ||
9571 element == EL_SHIELD_DEADLY) &&
9572 IS_ANIMATED(graphic))
9573 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9574 else if (IS_MOVING(x, y))
9575 ContinueMoving(x, y);
9576 else if (IS_ACTIVE_BOMB(element))
9577 CheckDynamite(x, y);
9578 else if (element == EL_AMOEBA_GROWING)
9579 AmoebeWaechst(x, y);
9580 else if (element == EL_AMOEBA_SHRINKING)
9581 AmoebaDisappearing(x, y);
9583 #if !USE_NEW_AMOEBA_CODE
9584 else if (IS_AMOEBALIVE(element))
9585 AmoebeAbleger(x, y);
9588 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9590 else if (element == EL_EXIT_CLOSED)
9592 else if (element == EL_SP_EXIT_CLOSED)
9594 else if (element == EL_EXPANDABLE_WALL_GROWING)
9596 else if (element == EL_EXPANDABLE_WALL ||
9597 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9598 element == EL_EXPANDABLE_WALL_VERTICAL ||
9599 element == EL_EXPANDABLE_WALL_ANY ||
9600 element == EL_BD_EXPANDABLE_WALL)
9602 else if (element == EL_FLAMES)
9603 CheckForDragon(x, y);
9604 else if (element == EL_EXPLOSION)
9605 ; /* drawing of correct explosion animation is handled separately */
9606 else if (element == EL_ELEMENT_SNAPPING ||
9607 element == EL_DIAGONAL_SHRINKING ||
9608 element == EL_DIAGONAL_GROWING)
9610 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9612 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9614 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9615 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9617 if (IS_BELT_ACTIVE(element))
9618 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9620 if (game.magic_wall_active)
9622 int jx = local_player->jx, jy = local_player->jy;
9624 /* play the element sound at the position nearest to the player */
9625 if ((element == EL_MAGIC_WALL_FULL ||
9626 element == EL_MAGIC_WALL_ACTIVE ||
9627 element == EL_MAGIC_WALL_EMPTYING ||
9628 element == EL_BD_MAGIC_WALL_FULL ||
9629 element == EL_BD_MAGIC_WALL_ACTIVE ||
9630 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9631 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9639 #if USE_NEW_AMOEBA_CODE
9640 /* new experimental amoeba growth stuff */
9641 if (!(FrameCounter % 8))
9643 static unsigned long random = 1684108901;
9645 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9647 x = RND(lev_fieldx);
9648 y = RND(lev_fieldy);
9649 element = Feld[x][y];
9651 if (!IS_PLAYER(x,y) &&
9652 (element == EL_EMPTY ||
9653 CAN_GROW_INTO(element) ||
9654 element == EL_QUICKSAND_EMPTY ||
9655 element == EL_ACID_SPLASH_LEFT ||
9656 element == EL_ACID_SPLASH_RIGHT))
9658 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9659 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9660 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9661 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9662 Feld[x][y] = EL_AMOEBA_DROP;
9665 random = random * 129 + 1;
9671 if (game.explosions_delayed)
9674 game.explosions_delayed = FALSE;
9676 SCAN_PLAYFIELD(x, y)
9678 element = Feld[x][y];
9680 if (ExplodeField[x][y])
9681 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9682 else if (element == EL_EXPLOSION)
9683 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9685 ExplodeField[x][y] = EX_TYPE_NONE;
9688 game.explosions_delayed = TRUE;
9691 if (game.magic_wall_active)
9693 if (!(game.magic_wall_time_left % 4))
9695 int element = Feld[magic_wall_x][magic_wall_y];
9697 if (element == EL_BD_MAGIC_WALL_FULL ||
9698 element == EL_BD_MAGIC_WALL_ACTIVE ||
9699 element == EL_BD_MAGIC_WALL_EMPTYING)
9700 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9702 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9705 if (game.magic_wall_time_left > 0)
9707 game.magic_wall_time_left--;
9708 if (!game.magic_wall_time_left)
9710 SCAN_PLAYFIELD(x, y)
9712 element = Feld[x][y];
9714 if (element == EL_MAGIC_WALL_ACTIVE ||
9715 element == EL_MAGIC_WALL_FULL)
9717 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9718 DrawLevelField(x, y);
9720 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9721 element == EL_BD_MAGIC_WALL_FULL)
9723 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9724 DrawLevelField(x, y);
9728 game.magic_wall_active = FALSE;
9733 if (game.light_time_left > 0)
9735 game.light_time_left--;
9737 if (game.light_time_left == 0)
9738 RedrawAllLightSwitchesAndInvisibleElements();
9741 if (game.timegate_time_left > 0)
9743 game.timegate_time_left--;
9745 if (game.timegate_time_left == 0)
9746 CloseAllOpenTimegates();
9749 if (game.lenses_time_left > 0)
9751 game.lenses_time_left--;
9753 if (game.lenses_time_left == 0)
9754 RedrawAllInvisibleElementsForLenses();
9757 if (game.magnify_time_left > 0)
9759 game.magnify_time_left--;
9761 if (game.magnify_time_left == 0)
9762 RedrawAllInvisibleElementsForMagnifier();
9765 for (i = 0; i < MAX_PLAYERS; i++)
9767 struct PlayerInfo *player = &stored_player[i];
9769 if (SHIELD_ON(player))
9771 if (player->shield_deadly_time_left)
9772 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9773 else if (player->shield_normal_time_left)
9774 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9781 PlayAllPlayersSound();
9783 if (options.debug) /* calculate frames per second */
9785 static unsigned long fps_counter = 0;
9786 static int fps_frames = 0;
9787 unsigned long fps_delay_ms = Counter() - fps_counter;
9791 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9793 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9796 fps_counter = Counter();
9799 redraw_mask |= REDRAW_FPS;
9802 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9804 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9806 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9808 local_player->show_envelope = 0;
9811 /* use random number generator in every frame to make it less predictable */
9812 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9816 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9818 int min_x = x, min_y = y, max_x = x, max_y = y;
9821 for (i = 0; i < MAX_PLAYERS; i++)
9823 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9825 if (!stored_player[i].active || &stored_player[i] == player)
9828 min_x = MIN(min_x, jx);
9829 min_y = MIN(min_y, jy);
9830 max_x = MAX(max_x, jx);
9831 max_y = MAX(max_y, jy);
9834 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9837 static boolean AllPlayersInVisibleScreen()
9841 for (i = 0; i < MAX_PLAYERS; i++)
9843 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9845 if (!stored_player[i].active)
9848 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9855 void ScrollLevel(int dx, int dy)
9857 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9860 BlitBitmap(drawto_field, drawto_field,
9861 FX + TILEX * (dx == -1) - softscroll_offset,
9862 FY + TILEY * (dy == -1) - softscroll_offset,
9863 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9864 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9865 FX + TILEX * (dx == 1) - softscroll_offset,
9866 FY + TILEY * (dy == 1) - softscroll_offset);
9870 x = (dx == 1 ? BX1 : BX2);
9871 for (y = BY1; y <= BY2; y++)
9872 DrawScreenField(x, y);
9877 y = (dy == 1 ? BY1 : BY2);
9878 for (x = BX1; x <= BX2; x++)
9879 DrawScreenField(x, y);
9882 redraw_mask |= REDRAW_FIELD;
9885 static boolean canFallDown(struct PlayerInfo *player)
9887 int jx = player->jx, jy = player->jy;
9889 return (IN_LEV_FIELD(jx, jy + 1) &&
9890 (IS_FREE(jx, jy + 1) ||
9891 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9892 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9893 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9896 static boolean canPassField(int x, int y, int move_dir)
9898 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9899 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9900 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9903 int element = Feld[x][y];
9905 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9906 !CAN_MOVE(element) &&
9907 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9908 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9909 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9912 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9914 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9915 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9916 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9920 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9921 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9922 (IS_DIGGABLE(Feld[newx][newy]) ||
9923 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9924 canPassField(newx, newy, move_dir)));
9927 static void CheckGravityMovement(struct PlayerInfo *player)
9929 #if USE_PLAYER_GRAVITY
9930 if (player->gravity && !player->programmed_action)
9932 if (game.gravity && !player->programmed_action)
9935 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9936 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9937 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9938 int jx = player->jx, jy = player->jy;
9939 boolean player_is_moving_to_valid_field =
9940 (!player_is_snapping &&
9941 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9942 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9943 boolean player_can_fall_down = canFallDown(player);
9945 if (player_can_fall_down &&
9946 !player_is_moving_to_valid_field)
9947 player->programmed_action = MV_DOWN;
9951 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9953 return CheckGravityMovement(player);
9955 #if USE_PLAYER_GRAVITY
9956 if (player->gravity && !player->programmed_action)
9958 if (game.gravity && !player->programmed_action)
9961 int jx = player->jx, jy = player->jy;
9962 boolean field_under_player_is_free =
9963 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9964 boolean player_is_standing_on_valid_field =
9965 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9966 (IS_WALKABLE(Feld[jx][jy]) &&
9967 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9969 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9970 player->programmed_action = MV_DOWN;
9976 -----------------------------------------------------------------------------
9977 dx, dy: direction (non-diagonal) to try to move the player to
9978 real_dx, real_dy: direction as read from input device (can be diagonal)
9981 boolean MovePlayerOneStep(struct PlayerInfo *player,
9982 int dx, int dy, int real_dx, int real_dy)
9984 int jx = player->jx, jy = player->jy;
9985 int new_jx = jx + dx, new_jy = jy + dy;
9986 #if !USE_FIXED_DONT_RUN_INTO
9990 boolean player_can_move = !player->cannot_move;
9992 if (!player->active || (!dx && !dy))
9993 return MP_NO_ACTION;
9995 player->MovDir = (dx < 0 ? MV_LEFT :
9998 dy > 0 ? MV_DOWN : MV_NONE);
10000 if (!IN_LEV_FIELD(new_jx, new_jy))
10001 return MP_NO_ACTION;
10003 if (!player_can_move)
10005 if (player->MovPos == 0)
10007 player->is_moving = FALSE;
10008 player->is_digging = FALSE;
10009 player->is_collecting = FALSE;
10010 player->is_snapping = FALSE;
10011 player->is_pushing = FALSE;
10016 if (!options.network && game.centered_player_nr == -1 &&
10017 !AllPlayersInSight(player, new_jx, new_jy))
10018 return MP_NO_ACTION;
10020 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10021 return MP_NO_ACTION;
10024 #if !USE_FIXED_DONT_RUN_INTO
10025 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10027 /* (moved to DigField()) */
10028 if (player_can_move && DONT_RUN_INTO(element))
10030 if (element == EL_ACID && dx == 0 && dy == 1)
10032 SplashAcid(new_jx, new_jy);
10033 Feld[jx][jy] = EL_PLAYER_1;
10034 InitMovingField(jx, jy, MV_DOWN);
10035 Store[jx][jy] = EL_ACID;
10036 ContinueMoving(jx, jy);
10037 BuryPlayer(player);
10040 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10046 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10047 if (can_move != MP_MOVING)
10050 /* check if DigField() has caused relocation of the player */
10051 if (player->jx != jx || player->jy != jy)
10052 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10054 StorePlayer[jx][jy] = 0;
10055 player->last_jx = jx;
10056 player->last_jy = jy;
10057 player->jx = new_jx;
10058 player->jy = new_jy;
10059 StorePlayer[new_jx][new_jy] = player->element_nr;
10061 if (player->move_delay_value_next != -1)
10063 player->move_delay_value = player->move_delay_value_next;
10064 player->move_delay_value_next = -1;
10068 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10070 player->step_counter++;
10072 PlayerVisit[jx][jy] = FrameCounter;
10074 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10075 player->is_moving = TRUE;
10079 /* should better be called in MovePlayer(), but this breaks some tapes */
10080 ScrollPlayer(player, SCROLL_INIT);
10086 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10088 int jx = player->jx, jy = player->jy;
10089 int old_jx = jx, old_jy = jy;
10090 int moved = MP_NO_ACTION;
10092 if (!player->active)
10097 if (player->MovPos == 0)
10099 player->is_moving = FALSE;
10100 player->is_digging = FALSE;
10101 player->is_collecting = FALSE;
10102 player->is_snapping = FALSE;
10103 player->is_pushing = FALSE;
10109 if (player->move_delay > 0)
10112 player->move_delay = -1; /* set to "uninitialized" value */
10114 /* store if player is automatically moved to next field */
10115 player->is_auto_moving = (player->programmed_action != MV_NONE);
10117 /* remove the last programmed player action */
10118 player->programmed_action = 0;
10120 if (player->MovPos)
10122 /* should only happen if pre-1.2 tape recordings are played */
10123 /* this is only for backward compatibility */
10125 int original_move_delay_value = player->move_delay_value;
10128 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10132 /* scroll remaining steps with finest movement resolution */
10133 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10135 while (player->MovPos)
10137 ScrollPlayer(player, SCROLL_GO_ON);
10138 ScrollScreen(NULL, SCROLL_GO_ON);
10140 AdvanceFrameAndPlayerCounters(player->index_nr);
10146 player->move_delay_value = original_move_delay_value;
10149 player->is_active = FALSE;
10151 if (player->last_move_dir & MV_HORIZONTAL)
10153 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10154 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10158 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10159 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10162 #if USE_FIXED_BORDER_RUNNING_GFX
10163 if (!moved && !player->is_active)
10165 player->is_moving = FALSE;
10166 player->is_digging = FALSE;
10167 player->is_collecting = FALSE;
10168 player->is_snapping = FALSE;
10169 player->is_pushing = FALSE;
10177 if (moved & MP_MOVING && !ScreenMovPos &&
10178 (player->index_nr == game.centered_player_nr ||
10179 game.centered_player_nr == -1))
10181 if (moved & MP_MOVING && !ScreenMovPos &&
10182 (player == local_player || !options.network))
10185 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10186 int offset = (setup.scroll_delay ? 3 : 0);
10188 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10190 /* actual player has left the screen -- scroll in that direction */
10191 if (jx != old_jx) /* player has moved horizontally */
10192 scroll_x += (jx - old_jx);
10193 else /* player has moved vertically */
10194 scroll_y += (jy - old_jy);
10198 if (jx != old_jx) /* player has moved horizontally */
10200 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10201 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10202 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10204 /* don't scroll over playfield boundaries */
10205 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10206 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10208 /* don't scroll more than one field at a time */
10209 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10211 /* don't scroll against the player's moving direction */
10212 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10213 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10214 scroll_x = old_scroll_x;
10216 else /* player has moved vertically */
10218 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10219 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10220 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10222 /* don't scroll over playfield boundaries */
10223 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10224 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10226 /* don't scroll more than one field at a time */
10227 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10229 /* don't scroll against the player's moving direction */
10230 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10231 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10232 scroll_y = old_scroll_y;
10236 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10239 if (!options.network && game.centered_player_nr == -1 &&
10240 !AllPlayersInVisibleScreen())
10242 scroll_x = old_scroll_x;
10243 scroll_y = old_scroll_y;
10247 if (!options.network && !AllPlayersInVisibleScreen())
10249 scroll_x = old_scroll_x;
10250 scroll_y = old_scroll_y;
10255 ScrollScreen(player, SCROLL_INIT);
10256 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10261 player->StepFrame = 0;
10263 if (moved & MP_MOVING)
10265 if (old_jx != jx && old_jy == jy)
10266 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10267 else if (old_jx == jx && old_jy != jy)
10268 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10270 DrawLevelField(jx, jy); /* for "crumbled sand" */
10272 player->last_move_dir = player->MovDir;
10273 player->is_moving = TRUE;
10274 player->is_snapping = FALSE;
10275 player->is_switching = FALSE;
10276 player->is_dropping = FALSE;
10277 player->is_dropping_pressed = FALSE;
10278 player->drop_pressed_delay = 0;
10281 /* should better be called here than above, but this breaks some tapes */
10282 ScrollPlayer(player, SCROLL_INIT);
10287 CheckGravityMovementWhenNotMoving(player);
10289 player->is_moving = FALSE;
10291 /* at this point, the player is allowed to move, but cannot move right now
10292 (e.g. because of something blocking the way) -- ensure that the player
10293 is also allowed to move in the next frame (in old versions before 3.1.1,
10294 the player was forced to wait again for eight frames before next try) */
10296 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10297 player->move_delay = 0; /* allow direct movement in the next frame */
10300 if (player->move_delay == -1) /* not yet initialized by DigField() */
10301 player->move_delay = player->move_delay_value;
10303 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10305 TestIfPlayerTouchesBadThing(jx, jy);
10306 TestIfPlayerTouchesCustomElement(jx, jy);
10309 if (!player->active)
10310 RemovePlayer(player);
10315 void ScrollPlayer(struct PlayerInfo *player, int mode)
10317 int jx = player->jx, jy = player->jy;
10318 int last_jx = player->last_jx, last_jy = player->last_jy;
10319 int move_stepsize = TILEX / player->move_delay_value;
10321 #if USE_NEW_PLAYER_SPEED
10322 if (!player->active)
10325 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10328 if (!player->active || player->MovPos == 0)
10332 if (mode == SCROLL_INIT)
10334 player->actual_frame_counter = FrameCounter;
10335 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10337 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10338 Feld[last_jx][last_jy] == EL_EMPTY)
10340 int last_field_block_delay = 0; /* start with no blocking at all */
10341 int block_delay_adjustment = player->block_delay_adjustment;
10343 /* if player blocks last field, add delay for exactly one move */
10344 if (player->block_last_field)
10346 last_field_block_delay += player->move_delay_value;
10348 /* when blocking enabled, prevent moving up despite gravity */
10349 #if USE_PLAYER_GRAVITY
10350 if (player->gravity && player->MovDir == MV_UP)
10351 block_delay_adjustment = -1;
10353 if (game.gravity && player->MovDir == MV_UP)
10354 block_delay_adjustment = -1;
10358 /* add block delay adjustment (also possible when not blocking) */
10359 last_field_block_delay += block_delay_adjustment;
10361 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10362 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10365 #if USE_NEW_PLAYER_SPEED
10366 if (player->MovPos != 0) /* player has not yet reached destination */
10372 else if (!FrameReached(&player->actual_frame_counter, 1))
10375 #if USE_NEW_PLAYER_SPEED
10376 if (player->MovPos != 0)
10378 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10379 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10381 /* before DrawPlayer() to draw correct player graphic for this case */
10382 if (player->MovPos == 0)
10383 CheckGravityMovement(player);
10386 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10387 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10389 /* before DrawPlayer() to draw correct player graphic for this case */
10390 if (player->MovPos == 0)
10391 CheckGravityMovement(player);
10394 if (player->MovPos == 0) /* player reached destination field */
10396 if (player->move_delay_reset_counter > 0)
10398 player->move_delay_reset_counter--;
10400 if (player->move_delay_reset_counter == 0)
10402 /* continue with normal speed after quickly moving through gate */
10403 HALVE_PLAYER_SPEED(player);
10405 /* be able to make the next move without delay */
10406 player->move_delay = 0;
10410 player->last_jx = jx;
10411 player->last_jy = jy;
10413 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10414 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10415 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10417 DrawPlayer(player); /* needed here only to cleanup last field */
10418 RemovePlayer(player);
10420 if (local_player->friends_still_needed == 0 ||
10421 IS_SP_ELEMENT(Feld[jx][jy]))
10422 PlayerWins(player);
10425 /* this breaks one level: "machine", level 000 */
10427 int move_direction = player->MovDir;
10428 int enter_side = MV_DIR_OPPOSITE(move_direction);
10429 int leave_side = move_direction;
10430 int old_jx = last_jx;
10431 int old_jy = last_jy;
10432 int old_element = Feld[old_jx][old_jy];
10433 int new_element = Feld[jx][jy];
10435 if (IS_CUSTOM_ELEMENT(old_element))
10436 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10438 player->index_bit, leave_side);
10440 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10441 CE_PLAYER_LEAVES_X,
10442 player->index_bit, leave_side);
10444 if (IS_CUSTOM_ELEMENT(new_element))
10445 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10446 player->index_bit, enter_side);
10448 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10449 CE_PLAYER_ENTERS_X,
10450 player->index_bit, enter_side);
10452 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10453 CE_MOVE_OF_X, move_direction);
10456 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10458 TestIfPlayerTouchesBadThing(jx, jy);
10459 TestIfPlayerTouchesCustomElement(jx, jy);
10461 /* needed because pushed element has not yet reached its destination,
10462 so it would trigger a change event at its previous field location */
10463 if (!player->is_pushing)
10464 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10466 if (!player->active)
10467 RemovePlayer(player);
10470 if (!local_player->LevelSolved && level.use_step_counter)
10480 if (TimeLeft <= 10 && setup.time_limit)
10481 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10483 DrawGameValue_Time(TimeLeft);
10485 if (!TimeLeft && setup.time_limit)
10486 for (i = 0; i < MAX_PLAYERS; i++)
10487 KillPlayer(&stored_player[i]);
10489 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10490 DrawGameValue_Time(TimePlayed);
10493 if (tape.single_step && tape.recording && !tape.pausing &&
10494 !player->programmed_action)
10495 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10499 void ScrollScreen(struct PlayerInfo *player, int mode)
10501 static unsigned long screen_frame_counter = 0;
10503 if (mode == SCROLL_INIT)
10505 /* set scrolling step size according to actual player's moving speed */
10506 ScrollStepSize = TILEX / player->move_delay_value;
10508 screen_frame_counter = FrameCounter;
10509 ScreenMovDir = player->MovDir;
10510 ScreenMovPos = player->MovPos;
10511 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10514 else if (!FrameReached(&screen_frame_counter, 1))
10519 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10520 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10521 redraw_mask |= REDRAW_FIELD;
10524 ScreenMovDir = MV_NONE;
10527 void TestIfPlayerTouchesCustomElement(int x, int y)
10529 static int xy[4][2] =
10536 static int trigger_sides[4][2] =
10538 /* center side border side */
10539 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10540 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10541 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10542 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10544 static int touch_dir[4] =
10546 MV_LEFT | MV_RIGHT,
10551 int center_element = Feld[x][y]; /* should always be non-moving! */
10554 for (i = 0; i < NUM_DIRECTIONS; i++)
10556 int xx = x + xy[i][0];
10557 int yy = y + xy[i][1];
10558 int center_side = trigger_sides[i][0];
10559 int border_side = trigger_sides[i][1];
10560 int border_element;
10562 if (!IN_LEV_FIELD(xx, yy))
10565 if (IS_PLAYER(x, y))
10567 struct PlayerInfo *player = PLAYERINFO(x, y);
10569 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10570 border_element = Feld[xx][yy]; /* may be moving! */
10571 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10572 border_element = Feld[xx][yy];
10573 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10574 border_element = MovingOrBlocked2Element(xx, yy);
10576 continue; /* center and border element do not touch */
10578 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10579 player->index_bit, border_side);
10580 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10581 CE_PLAYER_TOUCHES_X,
10582 player->index_bit, border_side);
10584 else if (IS_PLAYER(xx, yy))
10586 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10588 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10590 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10591 continue; /* center and border element do not touch */
10594 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10595 player->index_bit, center_side);
10596 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10597 CE_PLAYER_TOUCHES_X,
10598 player->index_bit, center_side);
10604 #if USE_ELEMENT_TOUCHING_BUGFIX
10606 void TestIfElementTouchesCustomElement(int x, int y)
10608 static int xy[4][2] =
10615 static int trigger_sides[4][2] =
10617 /* center side border side */
10618 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10619 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10620 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10621 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10623 static int touch_dir[4] =
10625 MV_LEFT | MV_RIGHT,
10630 boolean change_center_element = FALSE;
10631 int center_element = Feld[x][y]; /* should always be non-moving! */
10632 int border_element_old[NUM_DIRECTIONS];
10635 for (i = 0; i < NUM_DIRECTIONS; i++)
10637 int xx = x + xy[i][0];
10638 int yy = y + xy[i][1];
10639 int border_element;
10641 border_element_old[i] = -1;
10643 if (!IN_LEV_FIELD(xx, yy))
10646 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10647 border_element = Feld[xx][yy]; /* may be moving! */
10648 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10649 border_element = Feld[xx][yy];
10650 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10651 border_element = MovingOrBlocked2Element(xx, yy);
10653 continue; /* center and border element do not touch */
10655 border_element_old[i] = border_element;
10658 for (i = 0; i < NUM_DIRECTIONS; i++)
10660 int xx = x + xy[i][0];
10661 int yy = y + xy[i][1];
10662 int center_side = trigger_sides[i][0];
10663 int border_element = border_element_old[i];
10665 if (border_element == -1)
10668 /* check for change of border element */
10669 CheckElementChangeBySide(xx, yy, border_element, center_element,
10670 CE_TOUCHING_X, center_side);
10673 for (i = 0; i < NUM_DIRECTIONS; i++)
10675 int border_side = trigger_sides[i][1];
10676 int border_element = border_element_old[i];
10678 if (border_element == -1)
10681 /* check for change of center element (but change it only once) */
10682 if (!change_center_element)
10683 change_center_element =
10684 CheckElementChangeBySide(x, y, center_element, border_element,
10685 CE_TOUCHING_X, border_side);
10691 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10693 static int xy[4][2] =
10700 static int trigger_sides[4][2] =
10702 /* center side border side */
10703 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10704 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10705 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10706 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10708 static int touch_dir[4] =
10710 MV_LEFT | MV_RIGHT,
10715 boolean change_center_element = FALSE;
10716 int center_element = Feld[x][y]; /* should always be non-moving! */
10719 for (i = 0; i < NUM_DIRECTIONS; i++)
10721 int xx = x + xy[i][0];
10722 int yy = y + xy[i][1];
10723 int center_side = trigger_sides[i][0];
10724 int border_side = trigger_sides[i][1];
10725 int border_element;
10727 if (!IN_LEV_FIELD(xx, yy))
10730 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10731 border_element = Feld[xx][yy]; /* may be moving! */
10732 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10733 border_element = Feld[xx][yy];
10734 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10735 border_element = MovingOrBlocked2Element(xx, yy);
10737 continue; /* center and border element do not touch */
10739 /* check for change of center element (but change it only once) */
10740 if (!change_center_element)
10741 change_center_element =
10742 CheckElementChangeBySide(x, y, center_element, border_element,
10743 CE_TOUCHING_X, border_side);
10745 /* check for change of border element */
10746 CheckElementChangeBySide(xx, yy, border_element, center_element,
10747 CE_TOUCHING_X, center_side);
10753 void TestIfElementHitsCustomElement(int x, int y, int direction)
10755 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10756 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10757 int hitx = x + dx, hity = y + dy;
10758 int hitting_element = Feld[x][y];
10759 int touched_element;
10761 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10764 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10765 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10767 if (IN_LEV_FIELD(hitx, hity))
10769 int opposite_direction = MV_DIR_OPPOSITE(direction);
10770 int hitting_side = direction;
10771 int touched_side = opposite_direction;
10772 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10773 MovDir[hitx][hity] != direction ||
10774 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10780 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10781 CE_HITTING_X, touched_side);
10783 CheckElementChangeBySide(hitx, hity, touched_element,
10784 hitting_element, CE_HIT_BY_X, hitting_side);
10786 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10787 CE_HIT_BY_SOMETHING, opposite_direction);
10791 /* "hitting something" is also true when hitting the playfield border */
10792 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10793 CE_HITTING_SOMETHING, direction);
10797 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10799 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10800 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10801 int hitx = x + dx, hity = y + dy;
10802 int hitting_element = Feld[x][y];
10803 int touched_element;
10805 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10806 !IS_FREE(hitx, hity) &&
10807 (!IS_MOVING(hitx, hity) ||
10808 MovDir[hitx][hity] != direction ||
10809 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10812 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10816 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10820 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10821 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10823 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10824 EP_CAN_SMASH_EVERYTHING, direction);
10826 if (IN_LEV_FIELD(hitx, hity))
10828 int opposite_direction = MV_DIR_OPPOSITE(direction);
10829 int hitting_side = direction;
10830 int touched_side = opposite_direction;
10832 int touched_element = MovingOrBlocked2Element(hitx, hity);
10835 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10836 MovDir[hitx][hity] != direction ||
10837 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10846 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10847 CE_SMASHED_BY_SOMETHING, opposite_direction);
10849 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10850 CE_OTHER_IS_SMASHING, touched_side);
10852 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10853 CE_OTHER_GETS_SMASHED, hitting_side);
10859 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10861 int i, kill_x = -1, kill_y = -1;
10863 int bad_element = -1;
10864 static int test_xy[4][2] =
10871 static int test_dir[4] =
10879 for (i = 0; i < NUM_DIRECTIONS; i++)
10881 int test_x, test_y, test_move_dir, test_element;
10883 test_x = good_x + test_xy[i][0];
10884 test_y = good_y + test_xy[i][1];
10886 if (!IN_LEV_FIELD(test_x, test_y))
10890 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10892 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10894 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10895 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10897 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10898 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10902 bad_element = test_element;
10908 if (kill_x != -1 || kill_y != -1)
10910 if (IS_PLAYER(good_x, good_y))
10912 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10914 if (player->shield_deadly_time_left > 0 &&
10915 !IS_INDESTRUCTIBLE(bad_element))
10916 Bang(kill_x, kill_y);
10917 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10918 KillPlayer(player);
10921 Bang(good_x, good_y);
10925 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10927 int i, kill_x = -1, kill_y = -1;
10928 int bad_element = Feld[bad_x][bad_y];
10929 static int test_xy[4][2] =
10936 static int touch_dir[4] =
10938 MV_LEFT | MV_RIGHT,
10943 static int test_dir[4] =
10951 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10954 for (i = 0; i < NUM_DIRECTIONS; i++)
10956 int test_x, test_y, test_move_dir, test_element;
10958 test_x = bad_x + test_xy[i][0];
10959 test_y = bad_y + test_xy[i][1];
10960 if (!IN_LEV_FIELD(test_x, test_y))
10964 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10966 test_element = Feld[test_x][test_y];
10968 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10969 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10971 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10972 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10974 /* good thing is player or penguin that does not move away */
10975 if (IS_PLAYER(test_x, test_y))
10977 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10979 if (bad_element == EL_ROBOT && player->is_moving)
10980 continue; /* robot does not kill player if he is moving */
10982 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10984 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10985 continue; /* center and border element do not touch */
10992 else if (test_element == EL_PENGUIN)
11001 if (kill_x != -1 || kill_y != -1)
11003 if (IS_PLAYER(kill_x, kill_y))
11005 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11007 if (player->shield_deadly_time_left > 0 &&
11008 !IS_INDESTRUCTIBLE(bad_element))
11009 Bang(bad_x, bad_y);
11010 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11011 KillPlayer(player);
11014 Bang(kill_x, kill_y);
11018 void TestIfPlayerTouchesBadThing(int x, int y)
11020 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11023 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11025 TestIfGoodThingHitsBadThing(x, y, move_dir);
11028 void TestIfBadThingTouchesPlayer(int x, int y)
11030 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11033 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11035 TestIfBadThingHitsGoodThing(x, y, move_dir);
11038 void TestIfFriendTouchesBadThing(int x, int y)
11040 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11043 void TestIfBadThingTouchesFriend(int x, int y)
11045 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11048 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11050 int i, kill_x = bad_x, kill_y = bad_y;
11051 static int xy[4][2] =
11059 for (i = 0; i < NUM_DIRECTIONS; i++)
11063 x = bad_x + xy[i][0];
11064 y = bad_y + xy[i][1];
11065 if (!IN_LEV_FIELD(x, y))
11068 element = Feld[x][y];
11069 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11070 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11078 if (kill_x != bad_x || kill_y != bad_y)
11079 Bang(bad_x, bad_y);
11082 void KillPlayer(struct PlayerInfo *player)
11084 int jx = player->jx, jy = player->jy;
11086 if (!player->active)
11089 /* the following code was introduced to prevent an infinite loop when calling
11091 -> CheckTriggeredElementChangeExt()
11092 -> ExecuteCustomElementAction()
11094 -> (infinitely repeating the above sequence of function calls)
11095 which occurs when killing the player while having a CE with the setting
11096 "kill player X when explosion of <player X>"; the solution using a new
11097 field "player->killed" was chosen for backwards compatibility, although
11098 clever use of the fields "player->active" etc. would probably also work */
11099 if (player->killed)
11102 player->killed = TRUE;
11104 /* remove accessible field at the player's position */
11105 Feld[jx][jy] = EL_EMPTY;
11107 /* deactivate shield (else Bang()/Explode() would not work right) */
11108 player->shield_normal_time_left = 0;
11109 player->shield_deadly_time_left = 0;
11112 BuryPlayer(player);
11115 static void KillPlayerUnlessEnemyProtected(int x, int y)
11117 if (!PLAYER_ENEMY_PROTECTED(x, y))
11118 KillPlayer(PLAYERINFO(x, y));
11121 static void KillPlayerUnlessExplosionProtected(int x, int y)
11123 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11124 KillPlayer(PLAYERINFO(x, y));
11127 void BuryPlayer(struct PlayerInfo *player)
11129 int jx = player->jx, jy = player->jy;
11131 if (!player->active)
11134 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11135 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11137 player->GameOver = TRUE;
11138 RemovePlayer(player);
11141 void RemovePlayer(struct PlayerInfo *player)
11143 int jx = player->jx, jy = player->jy;
11144 int i, found = FALSE;
11146 player->present = FALSE;
11147 player->active = FALSE;
11149 if (!ExplodeField[jx][jy])
11150 StorePlayer[jx][jy] = 0;
11152 if (player->is_moving)
11153 DrawLevelField(player->last_jx, player->last_jy);
11155 for (i = 0; i < MAX_PLAYERS; i++)
11156 if (stored_player[i].active)
11160 AllPlayersGone = TRUE;
11166 #if USE_NEW_SNAP_DELAY
11167 static void setFieldForSnapping(int x, int y, int element, int direction)
11169 struct ElementInfo *ei = &element_info[element];
11170 int direction_bit = MV_DIR_TO_BIT(direction);
11171 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11172 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11173 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11175 Feld[x][y] = EL_ELEMENT_SNAPPING;
11176 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11178 ResetGfxAnimation(x, y);
11180 GfxElement[x][y] = element;
11181 GfxAction[x][y] = action;
11182 GfxDir[x][y] = direction;
11183 GfxFrame[x][y] = -1;
11188 =============================================================================
11189 checkDiagonalPushing()
11190 -----------------------------------------------------------------------------
11191 check if diagonal input device direction results in pushing of object
11192 (by checking if the alternative direction is walkable, diggable, ...)
11193 =============================================================================
11196 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11197 int x, int y, int real_dx, int real_dy)
11199 int jx, jy, dx, dy, xx, yy;
11201 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11204 /* diagonal direction: check alternative direction */
11209 xx = jx + (dx == 0 ? real_dx : 0);
11210 yy = jy + (dy == 0 ? real_dy : 0);
11212 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11216 =============================================================================
11218 -----------------------------------------------------------------------------
11219 x, y: field next to player (non-diagonal) to try to dig to
11220 real_dx, real_dy: direction as read from input device (can be diagonal)
11221 =============================================================================
11224 int DigField(struct PlayerInfo *player,
11225 int oldx, int oldy, int x, int y,
11226 int real_dx, int real_dy, int mode)
11228 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11229 boolean player_was_pushing = player->is_pushing;
11230 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11231 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11232 int jx = oldx, jy = oldy;
11233 int dx = x - jx, dy = y - jy;
11234 int nextx = x + dx, nexty = y + dy;
11235 int move_direction = (dx == -1 ? MV_LEFT :
11236 dx == +1 ? MV_RIGHT :
11238 dy == +1 ? MV_DOWN : MV_NONE);
11239 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11240 int dig_side = MV_DIR_OPPOSITE(move_direction);
11241 int old_element = Feld[jx][jy];
11242 #if USE_FIXED_DONT_RUN_INTO
11243 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11249 if (is_player) /* function can also be called by EL_PENGUIN */
11251 if (player->MovPos == 0)
11253 player->is_digging = FALSE;
11254 player->is_collecting = FALSE;
11257 if (player->MovPos == 0) /* last pushing move finished */
11258 player->is_pushing = FALSE;
11260 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11262 player->is_switching = FALSE;
11263 player->push_delay = -1;
11265 return MP_NO_ACTION;
11269 #if !USE_FIXED_DONT_RUN_INTO
11270 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11271 return MP_NO_ACTION;
11274 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11275 old_element = Back[jx][jy];
11277 /* in case of element dropped at player position, check background */
11278 else if (Back[jx][jy] != EL_EMPTY &&
11279 game.engine_version >= VERSION_IDENT(2,2,0,0))
11280 old_element = Back[jx][jy];
11282 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11283 return MP_NO_ACTION; /* field has no opening in this direction */
11285 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11286 return MP_NO_ACTION; /* field has no opening in this direction */
11288 #if USE_FIXED_DONT_RUN_INTO
11289 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11293 Feld[jx][jy] = player->artwork_element;
11294 InitMovingField(jx, jy, MV_DOWN);
11295 Store[jx][jy] = EL_ACID;
11296 ContinueMoving(jx, jy);
11297 BuryPlayer(player);
11299 return MP_DONT_RUN_INTO;
11303 #if USE_FIXED_DONT_RUN_INTO
11304 if (player_can_move && DONT_RUN_INTO(element))
11306 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11308 return MP_DONT_RUN_INTO;
11312 #if USE_FIXED_DONT_RUN_INTO
11313 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11314 return MP_NO_ACTION;
11317 #if !USE_FIXED_DONT_RUN_INTO
11318 element = Feld[x][y];
11321 collect_count = element_info[element].collect_count_initial;
11323 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11324 return MP_NO_ACTION;
11326 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11327 player_can_move = player_can_move_or_snap;
11329 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11330 game.engine_version >= VERSION_IDENT(2,2,0,0))
11332 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11333 player->index_bit, dig_side);
11334 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11335 player->index_bit, dig_side);
11337 if (Feld[x][y] != element) /* field changed by snapping */
11340 return MP_NO_ACTION;
11343 #if USE_PLAYER_GRAVITY
11344 if (player->gravity && is_player && !player->is_auto_moving &&
11345 canFallDown(player) && move_direction != MV_DOWN &&
11346 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11347 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11349 if (game.gravity && is_player && !player->is_auto_moving &&
11350 canFallDown(player) && move_direction != MV_DOWN &&
11351 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11352 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11355 if (player_can_move &&
11356 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11358 int sound_element = SND_ELEMENT(element);
11359 int sound_action = ACTION_WALKING;
11361 if (IS_RND_GATE(element))
11363 if (!player->key[RND_GATE_NR(element)])
11364 return MP_NO_ACTION;
11366 else if (IS_RND_GATE_GRAY(element))
11368 if (!player->key[RND_GATE_GRAY_NR(element)])
11369 return MP_NO_ACTION;
11371 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11373 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11374 return MP_NO_ACTION;
11376 else if (element == EL_EXIT_OPEN ||
11377 element == EL_SP_EXIT_OPEN ||
11378 element == EL_SP_EXIT_OPENING)
11380 sound_action = ACTION_PASSING; /* player is passing exit */
11382 else if (element == EL_EMPTY)
11384 sound_action = ACTION_MOVING; /* nothing to walk on */
11387 /* play sound from background or player, whatever is available */
11388 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11389 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11391 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11393 else if (player_can_move &&
11394 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11396 if (!ACCESS_FROM(element, opposite_direction))
11397 return MP_NO_ACTION; /* field not accessible from this direction */
11399 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11400 return MP_NO_ACTION;
11402 if (IS_EM_GATE(element))
11404 if (!player->key[EM_GATE_NR(element)])
11405 return MP_NO_ACTION;
11407 else if (IS_EM_GATE_GRAY(element))
11409 if (!player->key[EM_GATE_GRAY_NR(element)])
11410 return MP_NO_ACTION;
11412 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11414 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11415 return MP_NO_ACTION;
11417 else if (IS_EMC_GATE(element))
11419 if (!player->key[EMC_GATE_NR(element)])
11420 return MP_NO_ACTION;
11422 else if (IS_EMC_GATE_GRAY(element))
11424 if (!player->key[EMC_GATE_GRAY_NR(element)])
11425 return MP_NO_ACTION;
11427 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11429 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11430 return MP_NO_ACTION;
11432 else if (IS_SP_PORT(element))
11434 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11435 element == EL_SP_GRAVITY_PORT_RIGHT ||
11436 element == EL_SP_GRAVITY_PORT_UP ||
11437 element == EL_SP_GRAVITY_PORT_DOWN)
11438 #if USE_PLAYER_GRAVITY
11439 player->gravity = !player->gravity;
11441 game.gravity = !game.gravity;
11443 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11444 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11445 element == EL_SP_GRAVITY_ON_PORT_UP ||
11446 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11447 #if USE_PLAYER_GRAVITY
11448 player->gravity = TRUE;
11450 game.gravity = TRUE;
11452 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11453 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11454 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11455 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11456 #if USE_PLAYER_GRAVITY
11457 player->gravity = FALSE;
11459 game.gravity = FALSE;
11463 /* automatically move to the next field with double speed */
11464 player->programmed_action = move_direction;
11466 if (player->move_delay_reset_counter == 0)
11468 player->move_delay_reset_counter = 2; /* two double speed steps */
11470 DOUBLE_PLAYER_SPEED(player);
11473 PlayLevelSoundAction(x, y, ACTION_PASSING);
11475 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11479 if (mode != DF_SNAP)
11481 GfxElement[x][y] = GFX_ELEMENT(element);
11482 player->is_digging = TRUE;
11485 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11487 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11488 player->index_bit, dig_side);
11490 if (mode == DF_SNAP)
11492 #if USE_NEW_SNAP_DELAY
11493 if (level.block_snap_field)
11494 setFieldForSnapping(x, y, element, move_direction);
11496 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11498 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11501 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11502 player->index_bit, dig_side);
11505 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11509 if (is_player && mode != DF_SNAP)
11511 GfxElement[x][y] = element;
11512 player->is_collecting = TRUE;
11515 if (element == EL_SPEED_PILL)
11517 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11519 else if (element == EL_EXTRA_TIME && level.time > 0)
11521 TimeLeft += level.extra_time;
11522 DrawGameValue_Time(TimeLeft);
11524 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11526 player->shield_normal_time_left += level.shield_normal_time;
11527 if (element == EL_SHIELD_DEADLY)
11528 player->shield_deadly_time_left += level.shield_deadly_time;
11530 else if (element == EL_DYNAMITE ||
11531 element == EL_EM_DYNAMITE ||
11532 element == EL_SP_DISK_RED)
11534 if (player->inventory_size < MAX_INVENTORY_SIZE)
11535 player->inventory_element[player->inventory_size++] = element;
11537 DrawGameDoorValues();
11539 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11541 player->dynabomb_count++;
11542 player->dynabombs_left++;
11544 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11546 player->dynabomb_size++;
11548 else if (element == EL_DYNABOMB_INCREASE_POWER)
11550 player->dynabomb_xl = TRUE;
11552 else if (IS_KEY(element))
11554 player->key[KEY_NR(element)] = TRUE;
11556 DrawGameDoorValues();
11558 else if (IS_ENVELOPE(element))
11560 player->show_envelope = element;
11562 else if (element == EL_EMC_LENSES)
11564 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11566 RedrawAllInvisibleElementsForLenses();
11568 else if (element == EL_EMC_MAGNIFIER)
11570 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11572 RedrawAllInvisibleElementsForMagnifier();
11574 else if (IS_DROPPABLE(element) ||
11575 IS_THROWABLE(element)) /* can be collected and dropped */
11579 if (collect_count == 0)
11580 player->inventory_infinite_element = element;
11582 for (i = 0; i < collect_count; i++)
11583 if (player->inventory_size < MAX_INVENTORY_SIZE)
11584 player->inventory_element[player->inventory_size++] = element;
11586 DrawGameDoorValues();
11588 else if (collect_count > 0)
11590 local_player->gems_still_needed -= collect_count;
11591 if (local_player->gems_still_needed < 0)
11592 local_player->gems_still_needed = 0;
11594 DrawGameValue_Emeralds(local_player->gems_still_needed);
11597 RaiseScoreElement(element);
11598 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11601 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11602 player->index_bit, dig_side);
11604 if (mode == DF_SNAP)
11606 #if USE_NEW_SNAP_DELAY
11607 if (level.block_snap_field)
11608 setFieldForSnapping(x, y, element, move_direction);
11610 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11612 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11615 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11616 player->index_bit, dig_side);
11619 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11621 if (mode == DF_SNAP && element != EL_BD_ROCK)
11622 return MP_NO_ACTION;
11624 if (CAN_FALL(element) && dy)
11625 return MP_NO_ACTION;
11627 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11628 !(element == EL_SPRING && level.use_spring_bug))
11629 return MP_NO_ACTION;
11631 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11632 ((move_direction & MV_VERTICAL &&
11633 ((element_info[element].move_pattern & MV_LEFT &&
11634 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11635 (element_info[element].move_pattern & MV_RIGHT &&
11636 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11637 (move_direction & MV_HORIZONTAL &&
11638 ((element_info[element].move_pattern & MV_UP &&
11639 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11640 (element_info[element].move_pattern & MV_DOWN &&
11641 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11642 return MP_NO_ACTION;
11644 /* do not push elements already moving away faster than player */
11645 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11646 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11647 return MP_NO_ACTION;
11649 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11651 if (player->push_delay_value == -1 || !player_was_pushing)
11652 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11654 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11656 if (player->push_delay_value == -1)
11657 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11659 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11661 if (!player->is_pushing)
11662 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11665 player->is_pushing = TRUE;
11666 player->is_active = TRUE;
11668 if (!(IN_LEV_FIELD(nextx, nexty) &&
11669 (IS_FREE(nextx, nexty) ||
11670 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11671 IS_SB_ELEMENT(element)))))
11672 return MP_NO_ACTION;
11674 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11675 return MP_NO_ACTION;
11677 if (player->push_delay == -1) /* new pushing; restart delay */
11678 player->push_delay = 0;
11680 if (player->push_delay < player->push_delay_value &&
11681 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11682 element != EL_SPRING && element != EL_BALLOON)
11684 /* make sure that there is no move delay before next try to push */
11685 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11686 player->move_delay = 0;
11688 return MP_NO_ACTION;
11691 if (IS_SB_ELEMENT(element))
11693 if (element == EL_SOKOBAN_FIELD_FULL)
11695 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11696 local_player->sokobanfields_still_needed++;
11699 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11701 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11702 local_player->sokobanfields_still_needed--;
11705 Feld[x][y] = EL_SOKOBAN_OBJECT;
11707 if (Back[x][y] == Back[nextx][nexty])
11708 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11709 else if (Back[x][y] != 0)
11710 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11713 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11716 if (local_player->sokobanfields_still_needed == 0 &&
11717 game.emulation == EMU_SOKOBAN)
11719 PlayerWins(player);
11721 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11725 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11727 InitMovingField(x, y, move_direction);
11728 GfxAction[x][y] = ACTION_PUSHING;
11730 if (mode == DF_SNAP)
11731 ContinueMoving(x, y);
11733 MovPos[x][y] = (dx != 0 ? dx : dy);
11735 Pushed[x][y] = TRUE;
11736 Pushed[nextx][nexty] = TRUE;
11738 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11739 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11741 player->push_delay_value = -1; /* get new value later */
11743 /* check for element change _after_ element has been pushed */
11744 if (game.use_change_when_pushing_bug)
11746 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11747 player->index_bit, dig_side);
11748 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11749 player->index_bit, dig_side);
11752 else if (IS_SWITCHABLE(element))
11754 if (PLAYER_SWITCHING(player, x, y))
11756 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11757 player->index_bit, dig_side);
11762 player->is_switching = TRUE;
11763 player->switch_x = x;
11764 player->switch_y = y;
11766 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11768 if (element == EL_ROBOT_WHEEL)
11770 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11774 DrawLevelField(x, y);
11776 else if (element == EL_SP_TERMINAL)
11780 SCAN_PLAYFIELD(xx, yy)
11782 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11784 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11785 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11788 else if (IS_BELT_SWITCH(element))
11790 ToggleBeltSwitch(x, y);
11792 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11793 element == EL_SWITCHGATE_SWITCH_DOWN)
11795 ToggleSwitchgateSwitch(x, y);
11797 else if (element == EL_LIGHT_SWITCH ||
11798 element == EL_LIGHT_SWITCH_ACTIVE)
11800 ToggleLightSwitch(x, y);
11802 else if (element == EL_TIMEGATE_SWITCH)
11804 ActivateTimegateSwitch(x, y);
11806 else if (element == EL_BALLOON_SWITCH_LEFT ||
11807 element == EL_BALLOON_SWITCH_RIGHT ||
11808 element == EL_BALLOON_SWITCH_UP ||
11809 element == EL_BALLOON_SWITCH_DOWN ||
11810 element == EL_BALLOON_SWITCH_NONE ||
11811 element == EL_BALLOON_SWITCH_ANY)
11813 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11814 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11815 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11816 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11817 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11820 else if (element == EL_LAMP)
11822 Feld[x][y] = EL_LAMP_ACTIVE;
11823 local_player->lights_still_needed--;
11825 ResetGfxAnimation(x, y);
11826 DrawLevelField(x, y);
11828 else if (element == EL_TIME_ORB_FULL)
11830 Feld[x][y] = EL_TIME_ORB_EMPTY;
11832 if (level.time > 0 || level.use_time_orb_bug)
11834 TimeLeft += level.time_orb_time;
11835 DrawGameValue_Time(TimeLeft);
11838 ResetGfxAnimation(x, y);
11839 DrawLevelField(x, y);
11841 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11842 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11846 game.ball_state = !game.ball_state;
11848 SCAN_PLAYFIELD(xx, yy)
11850 int e = Feld[xx][yy];
11852 if (game.ball_state)
11854 if (e == EL_EMC_MAGIC_BALL)
11855 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11856 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11857 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11861 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11862 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11863 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11864 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11869 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11870 player->index_bit, dig_side);
11872 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11873 player->index_bit, dig_side);
11875 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11876 player->index_bit, dig_side);
11882 if (!PLAYER_SWITCHING(player, x, y))
11884 player->is_switching = TRUE;
11885 player->switch_x = x;
11886 player->switch_y = y;
11888 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11889 player->index_bit, dig_side);
11890 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11891 player->index_bit, dig_side);
11893 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11894 player->index_bit, dig_side);
11895 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11896 player->index_bit, dig_side);
11899 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11900 player->index_bit, dig_side);
11901 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11902 player->index_bit, dig_side);
11904 return MP_NO_ACTION;
11907 player->push_delay = -1;
11909 if (is_player) /* function can also be called by EL_PENGUIN */
11911 if (Feld[x][y] != element) /* really digged/collected something */
11913 player->is_collecting = !player->is_digging;
11914 player->is_active = TRUE;
11921 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11923 int jx = player->jx, jy = player->jy;
11924 int x = jx + dx, y = jy + dy;
11925 int snap_direction = (dx == -1 ? MV_LEFT :
11926 dx == +1 ? MV_RIGHT :
11928 dy == +1 ? MV_DOWN : MV_NONE);
11929 boolean can_continue_snapping = (level.continuous_snapping &&
11930 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11932 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11935 if (!player->active || !IN_LEV_FIELD(x, y))
11943 if (player->MovPos == 0)
11944 player->is_pushing = FALSE;
11946 player->is_snapping = FALSE;
11948 if (player->MovPos == 0)
11950 player->is_moving = FALSE;
11951 player->is_digging = FALSE;
11952 player->is_collecting = FALSE;
11958 #if USE_NEW_CONTINUOUS_SNAPPING
11959 /* prevent snapping with already pressed snap key when not allowed */
11960 if (player->is_snapping && !can_continue_snapping)
11963 if (player->is_snapping)
11967 player->MovDir = snap_direction;
11969 if (player->MovPos == 0)
11971 player->is_moving = FALSE;
11972 player->is_digging = FALSE;
11973 player->is_collecting = FALSE;
11976 player->is_dropping = FALSE;
11977 player->is_dropping_pressed = FALSE;
11978 player->drop_pressed_delay = 0;
11980 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11983 player->is_snapping = TRUE;
11984 player->is_active = TRUE;
11986 if (player->MovPos == 0)
11988 player->is_moving = FALSE;
11989 player->is_digging = FALSE;
11990 player->is_collecting = FALSE;
11993 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11994 DrawLevelField(player->last_jx, player->last_jy);
11996 DrawLevelField(x, y);
12001 boolean DropElement(struct PlayerInfo *player)
12003 int old_element, new_element;
12004 int dropx = player->jx, dropy = player->jy;
12005 int drop_direction = player->MovDir;
12006 int drop_side = drop_direction;
12007 int drop_element = (player->inventory_size > 0 ?
12008 player->inventory_element[player->inventory_size - 1] :
12009 player->inventory_infinite_element != EL_UNDEFINED ?
12010 player->inventory_infinite_element :
12011 player->dynabombs_left > 0 ?
12012 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12015 player->is_dropping_pressed = TRUE;
12017 /* do not drop an element on top of another element; when holding drop key
12018 pressed without moving, dropped element must move away before the next
12019 element can be dropped (this is especially important if the next element
12020 is dynamite, which can be placed on background for historical reasons) */
12021 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12024 if (IS_THROWABLE(drop_element))
12026 dropx += GET_DX_FROM_DIR(drop_direction);
12027 dropy += GET_DY_FROM_DIR(drop_direction);
12029 if (!IN_LEV_FIELD(dropx, dropy))
12033 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12034 new_element = drop_element; /* default: no change when dropping */
12036 /* check if player is active, not moving and ready to drop */
12037 if (!player->active || player->MovPos || player->drop_delay > 0)
12040 /* check if player has anything that can be dropped */
12041 if (new_element == EL_UNDEFINED)
12044 /* check if drop key was pressed long enough for EM style dynamite */
12045 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12048 /* check if anything can be dropped at the current position */
12049 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12052 /* collected custom elements can only be dropped on empty fields */
12053 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12056 if (old_element != EL_EMPTY)
12057 Back[dropx][dropy] = old_element; /* store old element on this field */
12059 ResetGfxAnimation(dropx, dropy);
12060 ResetRandomAnimationValue(dropx, dropy);
12062 if (player->inventory_size > 0 ||
12063 player->inventory_infinite_element != EL_UNDEFINED)
12065 if (player->inventory_size > 0)
12067 player->inventory_size--;
12069 DrawGameDoorValues();
12071 if (new_element == EL_DYNAMITE)
12072 new_element = EL_DYNAMITE_ACTIVE;
12073 else if (new_element == EL_EM_DYNAMITE)
12074 new_element = EL_EM_DYNAMITE_ACTIVE;
12075 else if (new_element == EL_SP_DISK_RED)
12076 new_element = EL_SP_DISK_RED_ACTIVE;
12079 Feld[dropx][dropy] = new_element;
12081 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12082 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12083 el2img(Feld[dropx][dropy]), 0);
12085 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12087 /* needed if previous element just changed to "empty" in the last frame */
12088 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12090 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12091 player->index_bit, drop_side);
12092 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12094 player->index_bit, drop_side);
12096 TestIfElementTouchesCustomElement(dropx, dropy);
12098 else /* player is dropping a dyna bomb */
12100 player->dynabombs_left--;
12102 Feld[dropx][dropy] = new_element;
12104 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12105 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12106 el2img(Feld[dropx][dropy]), 0);
12108 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12111 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12112 InitField_WithBug1(dropx, dropy, FALSE);
12114 new_element = Feld[dropx][dropy]; /* element might have changed */
12116 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12117 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12119 int move_direction, nextx, nexty;
12121 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12122 MovDir[dropx][dropy] = drop_direction;
12124 move_direction = MovDir[dropx][dropy];
12125 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12126 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12128 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12130 #if USE_FIX_IMPACT_COLLISION
12131 /* do not cause impact style collision by dropping elements that can fall */
12132 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12134 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12138 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12139 player->is_dropping = TRUE;
12141 player->drop_pressed_delay = 0;
12142 player->is_dropping_pressed = FALSE;
12144 player->drop_x = dropx;
12145 player->drop_y = dropy;
12150 /* ------------------------------------------------------------------------- */
12151 /* game sound playing functions */
12152 /* ------------------------------------------------------------------------- */
12154 static int *loop_sound_frame = NULL;
12155 static int *loop_sound_volume = NULL;
12157 void InitPlayLevelSound()
12159 int num_sounds = getSoundListSize();
12161 checked_free(loop_sound_frame);
12162 checked_free(loop_sound_volume);
12164 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12165 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12168 static void PlayLevelSound(int x, int y, int nr)
12170 int sx = SCREENX(x), sy = SCREENY(y);
12171 int volume, stereo_position;
12172 int max_distance = 8;
12173 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12175 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12176 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12179 if (!IN_LEV_FIELD(x, y) ||
12180 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12181 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12184 volume = SOUND_MAX_VOLUME;
12186 if (!IN_SCR_FIELD(sx, sy))
12188 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12189 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12191 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12194 stereo_position = (SOUND_MAX_LEFT +
12195 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12196 (SCR_FIELDX + 2 * max_distance));
12198 if (IS_LOOP_SOUND(nr))
12200 /* This assures that quieter loop sounds do not overwrite louder ones,
12201 while restarting sound volume comparison with each new game frame. */
12203 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12206 loop_sound_volume[nr] = volume;
12207 loop_sound_frame[nr] = FrameCounter;
12210 PlaySoundExt(nr, volume, stereo_position, type);
12213 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12215 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12216 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12217 y < LEVELY(BY1) ? LEVELY(BY1) :
12218 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12222 static void PlayLevelSoundAction(int x, int y, int action)
12224 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12227 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12229 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12231 if (sound_effect != SND_UNDEFINED)
12232 PlayLevelSound(x, y, sound_effect);
12235 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12238 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12240 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12241 PlayLevelSound(x, y, sound_effect);
12244 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12246 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12248 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12249 PlayLevelSound(x, y, sound_effect);
12252 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12254 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12256 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12257 StopSound(sound_effect);
12260 static void PlayLevelMusic()
12262 if (levelset.music[level_nr] != MUS_UNDEFINED)
12263 PlayMusic(levelset.music[level_nr]); /* from config file */
12265 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12268 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12270 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12271 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12272 int x = xx - 1 - offset;
12273 int y = yy - 1 - offset;
12278 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12282 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12286 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12290 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12294 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12298 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12302 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12305 case SAMPLE_android_clone:
12306 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12309 case SAMPLE_android_move:
12310 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12313 case SAMPLE_spring:
12314 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12318 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12322 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12325 case SAMPLE_eater_eat:
12326 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12330 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12333 case SAMPLE_collect:
12334 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12337 case SAMPLE_diamond:
12338 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12341 case SAMPLE_squash:
12342 /* !!! CHECK THIS !!! */
12344 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12346 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12350 case SAMPLE_wonderfall:
12351 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12355 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12359 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12363 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12367 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12371 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12375 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12378 case SAMPLE_wonder:
12379 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12383 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12386 case SAMPLE_exit_open:
12387 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12390 case SAMPLE_exit_leave:
12391 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12394 case SAMPLE_dynamite:
12395 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12399 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12403 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12407 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12411 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12415 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12419 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12423 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12429 void ChangeTime(int value)
12431 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12435 /* EMC game engine uses value from time counter of RND game engine */
12436 level.native_em_level->lev->time = *time;
12438 DrawGameValue_Time(*time);
12441 void RaiseScore(int value)
12443 /* EMC game engine and RND game engine have separate score counters */
12444 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12445 &level.native_em_level->lev->score : &local_player->score);
12449 DrawGameValue_Score(*score);
12453 void RaiseScore(int value)
12455 local_player->score += value;
12457 DrawGameValue_Score(local_player->score);
12460 void RaiseScoreElement(int element)
12465 case EL_BD_DIAMOND:
12466 case EL_EMERALD_YELLOW:
12467 case EL_EMERALD_RED:
12468 case EL_EMERALD_PURPLE:
12469 case EL_SP_INFOTRON:
12470 RaiseScore(level.score[SC_EMERALD]);
12473 RaiseScore(level.score[SC_DIAMOND]);
12476 RaiseScore(level.score[SC_CRYSTAL]);
12479 RaiseScore(level.score[SC_PEARL]);
12482 case EL_BD_BUTTERFLY:
12483 case EL_SP_ELECTRON:
12484 RaiseScore(level.score[SC_BUG]);
12487 case EL_BD_FIREFLY:
12488 case EL_SP_SNIKSNAK:
12489 RaiseScore(level.score[SC_SPACESHIP]);
12492 case EL_DARK_YAMYAM:
12493 RaiseScore(level.score[SC_YAMYAM]);
12496 RaiseScore(level.score[SC_ROBOT]);
12499 RaiseScore(level.score[SC_PACMAN]);
12502 RaiseScore(level.score[SC_NUT]);
12505 case EL_EM_DYNAMITE:
12506 case EL_SP_DISK_RED:
12507 case EL_DYNABOMB_INCREASE_NUMBER:
12508 case EL_DYNABOMB_INCREASE_SIZE:
12509 case EL_DYNABOMB_INCREASE_POWER:
12510 RaiseScore(level.score[SC_DYNAMITE]);
12512 case EL_SHIELD_NORMAL:
12513 case EL_SHIELD_DEADLY:
12514 RaiseScore(level.score[SC_SHIELD]);
12516 case EL_EXTRA_TIME:
12517 RaiseScore(level.extra_time_score);
12531 RaiseScore(level.score[SC_KEY]);
12534 RaiseScore(element_info[element].collect_score);
12539 void RequestQuitGame(boolean ask_if_really_quit)
12541 if (AllPlayersGone ||
12542 !ask_if_really_quit ||
12543 level_editor_test_game ||
12544 Request("Do you really want to quit the game ?",
12545 REQ_ASK | REQ_STAY_CLOSED))
12547 #if defined(NETWORK_AVALIABLE)
12548 if (options.network)
12549 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12553 if (!ask_if_really_quit || level_editor_test_game)
12555 game_status = GAME_MODE_MAIN;
12561 FadeOut(REDRAW_FIELD);
12563 game_status = GAME_MODE_MAIN;
12565 DrawAndFadeInMainMenu(REDRAW_FIELD);
12571 if (tape.playing && tape.deactivate_display)
12572 TapeDeactivateDisplayOff(TRUE);
12574 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12576 if (tape.playing && tape.deactivate_display)
12577 TapeDeactivateDisplayOn();
12582 /* ------------------------------------------------------------------------- */
12583 /* random generator functions */
12584 /* ------------------------------------------------------------------------- */
12586 unsigned int InitEngineRandom_RND(long seed)
12588 game.num_random_calls = 0;
12591 unsigned int rnd_seed = InitEngineRandom(seed);
12593 printf("::: START RND: %d\n", rnd_seed);
12598 return InitEngineRandom(seed);
12604 unsigned int RND(int max)
12608 game.num_random_calls++;
12610 return GetEngineRandom(max);
12617 /* ------------------------------------------------------------------------- */
12618 /* game engine snapshot handling functions */
12619 /* ------------------------------------------------------------------------- */
12621 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12623 struct EngineSnapshotInfo
12625 /* runtime values for custom element collect score */
12626 int collect_score[NUM_CUSTOM_ELEMENTS];
12628 /* runtime values for group element choice position */
12629 int choice_pos[NUM_GROUP_ELEMENTS];
12631 /* runtime values for belt position animations */
12632 int belt_graphic[4 * NUM_BELT_PARTS];
12633 int belt_anim_mode[4 * NUM_BELT_PARTS];
12636 struct EngineSnapshotNodeInfo
12643 static struct EngineSnapshotInfo engine_snapshot_rnd;
12644 static ListNode *engine_snapshot_list = NULL;
12645 static char *snapshot_level_identifier = NULL;
12646 static int snapshot_level_nr = -1;
12648 void FreeEngineSnapshot()
12650 while (engine_snapshot_list != NULL)
12651 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12654 setString(&snapshot_level_identifier, NULL);
12655 snapshot_level_nr = -1;
12658 static void SaveEngineSnapshotValues_RND()
12660 static int belt_base_active_element[4] =
12662 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12663 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12664 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12665 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12669 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12671 int element = EL_CUSTOM_START + i;
12673 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12676 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12678 int element = EL_GROUP_START + i;
12680 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12683 for (i = 0; i < 4; i++)
12685 for (j = 0; j < NUM_BELT_PARTS; j++)
12687 int element = belt_base_active_element[i] + j;
12688 int graphic = el2img(element);
12689 int anim_mode = graphic_info[graphic].anim_mode;
12691 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12692 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12697 static void LoadEngineSnapshotValues_RND()
12699 unsigned long num_random_calls = game.num_random_calls;
12702 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12704 int element = EL_CUSTOM_START + i;
12706 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12709 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12711 int element = EL_GROUP_START + i;
12713 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12716 for (i = 0; i < 4; i++)
12718 for (j = 0; j < NUM_BELT_PARTS; j++)
12720 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12721 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12723 graphic_info[graphic].anim_mode = anim_mode;
12727 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12729 InitRND(tape.random_seed);
12730 for (i = 0; i < num_random_calls; i++)
12734 if (game.num_random_calls != num_random_calls)
12736 Error(ERR_RETURN, "number of random calls out of sync");
12737 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12738 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12739 Error(ERR_EXIT, "this should not happen -- please debug");
12743 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12745 struct EngineSnapshotNodeInfo *bi =
12746 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12748 bi->buffer_orig = buffer;
12749 bi->buffer_copy = checked_malloc(size);
12752 memcpy(bi->buffer_copy, buffer, size);
12754 addNodeToList(&engine_snapshot_list, NULL, bi);
12757 void SaveEngineSnapshot()
12759 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12761 if (level_editor_test_game) /* do not save snapshots from editor */
12764 /* copy some special values to a structure better suited for the snapshot */
12766 SaveEngineSnapshotValues_RND();
12767 SaveEngineSnapshotValues_EM();
12769 /* save values stored in special snapshot structure */
12771 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12772 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12774 /* save further RND engine values */
12776 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12778 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12780 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12781 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12782 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12783 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12785 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12786 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12788 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12791 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12792 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12793 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12795 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12797 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12799 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12800 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12802 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12804 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12805 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12806 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12807 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12808 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12810 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12811 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12812 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12813 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12814 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12815 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12816 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12817 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
12818 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12819 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12821 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12822 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12824 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12825 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12826 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12828 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12829 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12831 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12832 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12833 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12834 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12835 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12837 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12838 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12840 /* save level identification information */
12842 setString(&snapshot_level_identifier, leveldir_current->identifier);
12843 snapshot_level_nr = level_nr;
12846 ListNode *node = engine_snapshot_list;
12849 while (node != NULL)
12851 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12856 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12860 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12862 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12865 void LoadEngineSnapshot()
12867 ListNode *node = engine_snapshot_list;
12869 if (engine_snapshot_list == NULL)
12872 while (node != NULL)
12874 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12879 /* restore special values from snapshot structure */
12881 LoadEngineSnapshotValues_RND();
12882 LoadEngineSnapshotValues_EM();
12885 boolean CheckEngineSnapshot()
12887 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12888 snapshot_level_nr == level_nr);
12892 /* ---------- new game button stuff ---------------------------------------- */
12894 /* graphic position values for game buttons */
12895 #define GAME_BUTTON_XSIZE 30
12896 #define GAME_BUTTON_YSIZE 30
12897 #define GAME_BUTTON_XPOS 5
12898 #define GAME_BUTTON_YPOS 215
12899 #define SOUND_BUTTON_XPOS 5
12900 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12902 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12903 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12904 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12905 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12906 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12907 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12914 } gamebutton_info[NUM_GAME_BUTTONS] =
12917 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12922 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12923 GAME_CTRL_ID_PAUSE,
12927 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12932 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12933 SOUND_CTRL_ID_MUSIC,
12934 "background music on/off"
12937 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12938 SOUND_CTRL_ID_LOOPS,
12939 "sound loops on/off"
12942 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12943 SOUND_CTRL_ID_SIMPLE,
12944 "normal sounds on/off"
12948 void CreateGameButtons()
12952 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12954 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12955 struct GadgetInfo *gi;
12958 unsigned long event_mask;
12959 int gd_xoffset, gd_yoffset;
12960 int gd_x1, gd_x2, gd_y1, gd_y2;
12963 gd_xoffset = gamebutton_info[i].x;
12964 gd_yoffset = gamebutton_info[i].y;
12965 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12966 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12968 if (id == GAME_CTRL_ID_STOP ||
12969 id == GAME_CTRL_ID_PAUSE ||
12970 id == GAME_CTRL_ID_PLAY)
12972 button_type = GD_TYPE_NORMAL_BUTTON;
12974 event_mask = GD_EVENT_RELEASED;
12975 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12976 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12980 button_type = GD_TYPE_CHECK_BUTTON;
12982 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12983 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12984 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12985 event_mask = GD_EVENT_PRESSED;
12986 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12987 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12990 gi = CreateGadget(GDI_CUSTOM_ID, id,
12991 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12992 GDI_X, DX + gd_xoffset,
12993 GDI_Y, DY + gd_yoffset,
12994 GDI_WIDTH, GAME_BUTTON_XSIZE,
12995 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12996 GDI_TYPE, button_type,
12997 GDI_STATE, GD_BUTTON_UNPRESSED,
12998 GDI_CHECKED, checked,
12999 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13000 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13001 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13002 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13003 GDI_EVENT_MASK, event_mask,
13004 GDI_CALLBACK_ACTION, HandleGameButtons,
13008 Error(ERR_EXIT, "cannot create gadget");
13010 game_gadget[id] = gi;
13014 void FreeGameButtons()
13018 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13019 FreeGadget(game_gadget[i]);
13022 static void MapGameButtons()
13026 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13027 MapGadget(game_gadget[i]);
13030 void UnmapGameButtons()
13034 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13035 UnmapGadget(game_gadget[i]);
13038 static void HandleGameButtons(struct GadgetInfo *gi)
13040 int id = gi->custom_id;
13042 if (game_status != GAME_MODE_PLAYING)
13047 case GAME_CTRL_ID_STOP:
13051 RequestQuitGame(TRUE);
13054 case GAME_CTRL_ID_PAUSE:
13055 if (options.network)
13057 #if defined(NETWORK_AVALIABLE)
13059 SendToServer_ContinuePlaying();
13061 SendToServer_PausePlaying();
13065 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13068 case GAME_CTRL_ID_PLAY:
13071 #if defined(NETWORK_AVALIABLE)
13072 if (options.network)
13073 SendToServer_ContinuePlaying();
13077 tape.pausing = FALSE;
13078 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13083 case SOUND_CTRL_ID_MUSIC:
13084 if (setup.sound_music)
13086 setup.sound_music = FALSE;
13089 else if (audio.music_available)
13091 setup.sound = setup.sound_music = TRUE;
13093 SetAudioMode(setup.sound);
13099 case SOUND_CTRL_ID_LOOPS:
13100 if (setup.sound_loops)
13101 setup.sound_loops = FALSE;
13102 else if (audio.loops_available)
13104 setup.sound = setup.sound_loops = TRUE;
13105 SetAudioMode(setup.sound);
13109 case SOUND_CTRL_ID_SIMPLE:
13110 if (setup.sound_simple)
13111 setup.sound_simple = FALSE;
13112 else if (audio.sound_available)
13114 setup.sound = setup.sound_simple = TRUE;
13115 SetAudioMode(setup.sound);